Bug 1090501 - pre-emptively open the camera hardware for certified apps with 'camera' permission, r=fabrice

This commit is contained in:
Fabrice Desre 2014-10-31 13:54:02 -04:00
parent bdee1cad15
commit 97c08f1824
8 changed files with 255 additions and 6 deletions

View File

@ -5,10 +5,14 @@
#include "CameraPreferences.h"
#include "CameraCommon.h"
#include "DOMCameraManager.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/Monitor.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Preferences.h"
#ifdef MOZ_WIDGET_GONK
#include "nsIObserverService.h"
#endif
using namespace mozilla;
@ -26,6 +30,12 @@ uint32_t CameraPreferences::sPrefCameraControlLowMemoryThresholdMB = 0;
bool CameraPreferences::sPrefCameraParametersIsLowMemory = false;
#ifdef MOZ_WIDGET_GONK
StaticRefPtr<CameraPreferences> CameraPreferences::sObserver;
NS_IMPL_ISUPPORTS(CameraPreferences, nsIObserver);
#endif
#ifdef CAMERAPREFERENCES_HAVE_SEPARATE_UINT32_AND_NSRESULT
/* static */
nsresult
@ -205,6 +215,19 @@ CameraPreferences::Initialize()
nsresult rv;
#ifdef MOZ_WIDGET_GONK
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
sObserver = new CameraPreferences();
rv = obs->AddObserver(sObserver, "init-camera-hw", false);
if (NS_WARN_IF(NS_FAILED(rv))) {
sObserver = nullptr;
}
} else {
DOM_CAMERA_LOGE("Could not get observer service\n");
}
#endif
sPrefMonitor = new Monitor("CameraPreferences.sPrefMonitor");
sPrefTestEnabled = new nsCString();
@ -239,9 +262,42 @@ CameraPreferences::Shutdown()
sPrefGonkParameters = nullptr;
sPrefMonitor = nullptr;
#ifdef MOZ_WIDGET_GONK
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
nsresult rv = obs->RemoveObserver(sObserver , "init-camera-hw");
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to remove CameraPreferences observer (0x%x)\n", rv);
}
sObserver = nullptr;
} else {
DOM_CAMERA_LOGE("Could not get observer service\n");
}
#endif
DOM_CAMERA_LOGI("Camera preferences shut down\n");
}
#ifdef MOZ_WIDGET_GONK
nsresult
CameraPreferences::PreinitCameraHardware()
{
nsDOMCameraManager::PreinitCameraHardware();
return NS_OK;
}
NS_IMETHODIMP
CameraPreferences::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
{
if (strcmp(aTopic, "init-camera-hw") == 0) {
return PreinitCameraHardware();
}
DOM_CAMERA_LOGE("Got unhandled topic '%s'\n", aTopic);
return NS_OK;
}
#endif
/* static */
bool
CameraPreferences::GetPref(const char* aPref, nsACString& aVal)

View File

@ -7,6 +7,7 @@
#define DOM_CAMERA_CAMERAPREFERENCES_H
#include "nsString.h"
#include "nsIObserver.h"
#if defined(MOZ_HAVE_CXX11_STRONG_ENUMS) || defined(MOZ_HAVE_CXX11_ENUM_TYPE)
// Older compilers that don't support strongly-typed enums
@ -20,8 +21,16 @@ namespace mozilla {
template<class T> class StaticAutoPtr;
class CameraPreferences
#ifdef MOZ_WIDGET_GONK
: public nsIObserver
#endif
{
public:
#ifdef MOZ_WIDGET_GONK
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
#endif
static bool Initialize();
static void Shutdown();
@ -79,10 +88,21 @@ protected:
static bool sPrefCameraParametersIsLowMemory;
#ifdef MOZ_WIDGET_GONK
static StaticRefPtr<CameraPreferences> sObserver;
nsresult PreinitCameraHardware();
protected:
// Objects may be instantiated for use as observers.
CameraPreferences() { }
~CameraPreferences() { }
#else
private:
// static class only
// Static class only.
CameraPreferences();
~CameraPreferences();
#endif
};
} // namespace mozilla

View File

@ -46,6 +46,12 @@ using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::ipc;
#ifdef MOZ_WIDGET_GONK
StaticRefPtr<ICameraControl> nsDOMCameraControl::sCachedCameraControl;
/* static */ nsresult nsDOMCameraControl::sCachedCameraControlStartResult = NS_OK;
/* static */ nsCOMPtr<nsITimer> nsDOMCameraControl::sDiscardCachedCameraControlTimer;
#endif
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
@ -144,6 +150,60 @@ nsDOMCameraControl::DOMCameraConfiguration::~DOMCameraConfiguration()
MOZ_COUNT_DTOR(nsDOMCameraControl::DOMCameraConfiguration);
}
#ifdef MOZ_WIDGET_GONK
// This shoudl be long enough for even our slowest platforms.
static const unsigned long kCachedCameraTimeoutMs = 3500;
// Open the battery-door-facing camera by default.
static const uint32_t kDefaultCameraId = 0;
/* static */ void
nsDOMCameraControl::PreinitCameraHardware()
{
// Assume a default, minimal configuration. This should initialize the
// hardware, but won't (can't) start the preview.
nsRefPtr<ICameraControl> cameraControl = ICameraControl::Create(kDefaultCameraId);
if (NS_WARN_IF(!cameraControl)) {
return;
}
sCachedCameraControlStartResult = cameraControl->Start();
if (NS_WARN_IF(NS_FAILED(sCachedCameraControlStartResult))) {
return;
}
sCachedCameraControl = cameraControl;
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
if (NS_WARN_IF(!timer)) {
return;
}
nsresult rv = timer->InitWithFuncCallback(DiscardCachedCameraInstance,
nullptr,
kCachedCameraTimeoutMs,
nsITimer::TYPE_ONE_SHOT);
if (NS_WARN_IF(NS_FAILED(rv))) {
// If we can't start the timer, it's possible for an app to never grab the
// camera, leaving the hardware tied up indefinitely. Better to take the
// performance hit.
sCachedCameraControl = nullptr;
return;
}
sDiscardCachedCameraControlTimer = timer;
}
/* static */ void
nsDOMCameraControl::DiscardCachedCameraInstance(nsITimer* aTimer, void* aClosure)
{
MOZ_ASSERT(NS_IsMainThread());
sDiscardCachedCameraControlTimer = nullptr;
sCachedCameraControl = nullptr;
}
#endif
nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
const CameraConfiguration& aInitialConfig,
GetCameraCallback* aOnSuccess,
@ -214,7 +274,19 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
config.mRecorderProfile = aInitialConfig.mRecorderProfile;
}
mCameraControl = ICameraControl::Create(aCameraId);
#ifdef MOZ_WIDGET_GONK
bool gotCached = false;
if (sCachedCameraControl && aCameraId == kDefaultCameraId) {
mCameraControl = sCachedCameraControl;
sCachedCameraControl = nullptr;
gotCached = true;
} else {
sCachedCameraControl = nullptr;
#endif
mCameraControl = ICameraControl::Create(aCameraId);
#ifdef MOZ_WIDGET_GONK
}
#endif
mCurrentConfiguration = initialConfig.forget();
// Attach our DOM-facing media stream to our viewfinder stream.
@ -228,12 +300,20 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
mListener = new DOMCameraControlListener(this, mInput);
mCameraControl->AddListener(mListener);
// Start the camera...
if (haveInitialConfig) {
rv = mCameraControl->Start(&config);
#ifdef MOZ_WIDGET_GONK
if (!gotCached || NS_FAILED(sCachedCameraControlStartResult)) {
#endif
// Start the camera...
if (haveInitialConfig) {
rv = mCameraControl->Start(&config);
} else {
rv = mCameraControl->Start();
}
#ifdef MOZ_WIDGET_GONK
} else {
rv = mCameraControl->Start();
rv = mCameraControl->SetConfiguration(config);
}
#endif
if (NS_FAILED(rv)) {
mListener->OnUserError(DOMCameraControlListener::kInStartCamera, rv);
}

View File

@ -18,6 +18,9 @@
#include "nsHashPropertyBag.h"
#include "DeviceStorage.h"
#include "DOMCameraControlListener.h"
#ifdef MOZ_WIDGET_GONK
#include "nsITimer.h"
#endif
class nsDOMDeviceStorage;
class nsPIDOMWindow;
@ -140,6 +143,11 @@ public:
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
#ifdef MOZ_WIDGET_GONK
static void PreinitCameraHardware();
static void DiscardCachedCameraInstance(nsITimer* aTimer, void* aClosure);
#endif
IMPL_EVENT_HANDLER(facesdetected)
IMPL_EVENT_HANDLER(shutter)
IMPL_EVENT_HANDLER(close)
@ -253,6 +261,13 @@ protected:
nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
DOMCameraControlListener::PreviewState mPreviewState;
#ifdef MOZ_WIDGET_GONK
// cached camera control, to improve start-up time
static StaticRefPtr<ICameraControl> sCachedCameraControl;
static nsresult sCachedCameraControlStartResult;
static nsCOMPtr<nsITimer> sDiscardCachedCameraControlTimer;
#endif
private:
nsDOMCameraControl(const nsDOMCameraControl&) MOZ_DELETE;
nsDOMCameraControl& operator=(const nsDOMCameraControl&) MOZ_DELETE;

View File

@ -264,6 +264,14 @@ CameraPermissionRequest::GetTypes(nsIArray** aTypes)
aTypes);
}
#ifdef MOZ_WIDGET_GONK
/* static */ void
nsDOMCameraManager::PreinitCameraHardware()
{
nsDOMCameraControl::PreinitCameraHardware();
}
#endif
already_AddRefed<Promise>
nsDOMCameraManager::GetCamera(const nsAString& aCamera,
const CameraConfiguration& aInitialConfig,

View File

@ -89,6 +89,10 @@ public:
virtual JSObject* WrapObject(JSContext* aCx)
MOZ_OVERRIDE;
#ifdef MOZ_WIDGET_GONK
static void PreinitCameraHardware();
#endif
protected:
void XpComShutdown();
void Shutdown(uint64_t aWindowId);

View File

@ -1814,6 +1814,64 @@ TabChild::DoFakeShow()
mDidFakeShow = true;
}
#ifdef MOZ_WIDGET_GONK
void
TabChild::MaybeRequestPreinitCamera()
{
// Check if this tab will use the `camera` permission.
nsCOMPtr<nsIAppsService> appsService = do_GetService("@mozilla.org/AppsService;1");
if (NS_WARN_IF(!appsService)) {
return;
}
nsString manifestUrl = EmptyString();
appsService->GetManifestURLByLocalId(OwnAppId(), manifestUrl);
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
if (NS_WARN_IF(!secMan)) {
return;
}
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), manifestUrl);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
nsCOMPtr<nsIPrincipal> principal;
rv = secMan->GetAppCodebasePrincipal(uri, OwnAppId(), false,
getter_AddRefs(principal));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
uint16_t status = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
principal->GetAppStatus(&status);
bool isCertified = status == nsIPrincipal::APP_STATUS_CERTIFIED;
if (!isCertified) {
return;
}
nsCOMPtr<nsIPermissionManager> permMgr = services::GetPermissionManager();
if (NS_WARN_IF(!permMgr)) {
return;
}
uint32_t permission = nsIPermissionManager::DENY_ACTION;
permMgr->TestPermissionFromPrincipal(principal, "camera", &permission);
bool hasPermission = permission == nsIPermissionManager::ALLOW_ACTION;
if (!hasPermission) {
return;
}
nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
if (NS_WARN_IF(!observerService)) {
return;
}
observerService->NotifyObservers(nullptr, "init-camera-hw", nullptr);
}
#endif
bool
TabChild::RecvShow(const nsIntSize& size)
{
@ -1838,6 +1896,10 @@ TabChild::RecvShow(const nsIntSize& size)
baseWindow->SetVisibility(true);
#ifdef MOZ_WIDGET_GONK
MaybeRequestPreinitCamera();
#endif
return InitTabChildGlobal();
}

View File

@ -498,6 +498,10 @@ protected:
virtual bool RecvRequestNotifyAfterRemotePaint();
#ifdef MOZ_WIDGET_GONK
void MaybeRequestPreinitCamera();
#endif
private:
/**
* Create a new TabChild object.