aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authord34d <clark@cyngn.com>2016-03-08 09:14:34 -0800
committerClark Scheff <clark@cyngn.com>2016-03-29 15:02:45 -0700
commitbfa500dd1551b319320bd625680b77b702b6b771 (patch)
tree17f6ae2982b108d6cc1f68ae83127fb28a2267d1
parent11334c9592d095b8bdfa8f849e59e8e9856a6301 (diff)
downloadvendor_cmsdk-bfa500dd1551b319320bd625680b77b702b6b771.zip
vendor_cmsdk-bfa500dd1551b319320bd625680b77b702b6b771.tar.gz
vendor_cmsdk-bfa500dd1551b319320bd625680b77b702b6b771.tar.bz2
LLS: Add live lock screen service [1/4]
The live lock screen service is resposonsible for deciding what LLS should be displayed at any given time. Live lock screens can be swapped out using a priority based system. Change-Id: Ifba73e839b749fe78a9e4ee347dd20eea6bf0a22
-rw-r--r--api/cm_current.txt33
-rw-r--r--cm/lib/main/java/org/cyanogenmod/platform/internal/LiveLockScreenServiceBroker.java415
-rw-r--r--cm/res/AndroidManifest.xml20
-rw-r--r--cm/res/res/values/config.xml1
-rw-r--r--cm/res/res/values/strings.xml15
-rw-r--r--src/java/cyanogenmod/app/BaseLiveLockManagerService.java226
-rw-r--r--src/java/cyanogenmod/app/CMContextConstants.java13
-rw-r--r--src/java/cyanogenmod/app/ILiveLockScreenChangeListener.aidl27
-rw-r--r--src/java/cyanogenmod/app/ILiveLockScreenManager.aidl73
-rw-r--r--src/java/cyanogenmod/app/ILiveLockScreenManagerProvider.aidl65
-rw-r--r--src/java/cyanogenmod/app/LiveLockScreenInfo.aidl19
-rw-r--r--src/java/cyanogenmod/app/LiveLockScreenInfo.java196
-rw-r--r--src/java/cyanogenmod/app/LiveLockScreenManager.java182
-rw-r--r--src/java/cyanogenmod/providers/CMSettings.java7
-rw-r--r--system-api/cm_system-current.txt33
15 files changed, 1325 insertions, 0 deletions
diff --git a/api/cm_current.txt b/api/cm_current.txt
index e906245..a8b7319 100644
--- a/api/cm_current.txt
+++ b/api/cm_current.txt
@@ -196,6 +196,38 @@ package cyanogenmod.app {
field public static final java.lang.String SERVICE_INTERFACE = "cyanogenmod.app.CustomTileListenerService";
}
+ public class LiveLockScreenInfo implements android.os.Parcelable {
+ ctor public LiveLockScreenInfo(android.content.ComponentName, int);
+ ctor public LiveLockScreenInfo();
+ method public cyanogenmod.app.LiveLockScreenInfo clone();
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<cyanogenmod.app.LiveLockScreenInfo> CREATOR;
+ field public static final int PRIORITY_DEFAULT = 0; // 0x0
+ field public static final int PRIORITY_HIGH = 1; // 0x1
+ field public static final int PRIORITY_LOW = -1; // 0xffffffff
+ field public static final int PRIORITY_MAX = 2; // 0x2
+ field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+ field public android.content.ComponentName component;
+ field public int priority;
+ }
+
+ public static class LiveLockScreenInfo.Builder {
+ ctor public LiveLockScreenInfo.Builder();
+ method public cyanogenmod.app.LiveLockScreenInfo build();
+ method public cyanogenmod.app.LiveLockScreenInfo.Builder setComponent(android.content.ComponentName);
+ method public cyanogenmod.app.LiveLockScreenInfo.Builder setPriority(int);
+ }
+
+ public class LiveLockScreenManager {
+ method public void cancel(int);
+ method public cyanogenmod.app.LiveLockScreenInfo getDefaultLiveLockScreen();
+ method public static cyanogenmod.app.LiveLockScreenManager getInstance(android.content.Context);
+ method public void setDefaultLiveLockScreen(cyanogenmod.app.LiveLockScreenInfo);
+ method public boolean show(int, cyanogenmod.app.LiveLockScreenInfo);
+ field public static final java.lang.String SERVICE_INTERFACE = "cyanogenmod.app.LiveLockScreenManagerService";
+ }
+
public class PartnerInterface {
method public java.lang.String getCurrentHotwordPackageName();
method public static cyanogenmod.app.PartnerInterface getInstance(android.content.Context);
@@ -564,6 +596,7 @@ package cyanogenmod.platform {
field public static final java.lang.String ACCESS_APP_SUGGESTIONS = "cyanogenmod.permission.ACCESS_APP_SUGGESTIONS";
field public static final java.lang.String ACCESS_THEME_MANAGER = "cyanogenmod.permission.ACCESS_THEME_MANAGER";
field public static final java.lang.String HARDWARE_ABSTRACTION_ACCESS = "cyanogenmod.permission.HARDWARE_ABSTRACTION_ACCESS";
+ field public static final java.lang.String LIVE_LOCK_SCREEN_MANAGER_ACCESS = "cyanogenmod.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS";
field public static final java.lang.String MANAGE_ALARMS = "cyanogenmod.permission.MANAGE_ALARMS";
field public static final java.lang.String MANAGE_PERSISTENT_STORAGE = "cyanogenmod.permission.MANAGE_PERSISTENT_STORAGE";
field public static final java.lang.String MODIFY_MSIM_PHONE_STATE = "cyanogenmod.permission.MODIFY_MSIM_PHONE_STATE";
diff --git a/cm/lib/main/java/org/cyanogenmod/platform/internal/LiveLockScreenServiceBroker.java b/cm/lib/main/java/org/cyanogenmod/platform/internal/LiveLockScreenServiceBroker.java
new file mode 100644
index 0000000..e85e683
--- /dev/null
+++ b/cm/lib/main/java/org/cyanogenmod/platform/internal/LiveLockScreenServiceBroker.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.cyanogenmod.platform.internal;
+
+import android.annotation.Nullable;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.util.Slog;
+
+import com.android.server.SystemService;
+
+import cyanogenmod.app.CMContextConstants;
+import cyanogenmod.app.ILiveLockScreenChangeListener;
+import cyanogenmod.app.ILiveLockScreenManager;
+import cyanogenmod.app.ILiveLockScreenManagerProvider;
+import cyanogenmod.app.LiveLockScreenInfo;
+import cyanogenmod.app.LiveLockScreenManager;
+import cyanogenmod.platform.Manifest;
+import cyanogenmod.providers.CMSettings;
+
+import java.util.List;
+
+/**
+ * Live lock screen service broker for connecting clients to a backing Live lock screen manager
+ * service.
+ *
+ * @hide
+ */
+public class LiveLockScreenServiceBroker extends SystemService {
+ private static final String TAG = LiveLockScreenServiceBroker.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private static final int MSG_TRY_CONNECTING = 1;
+
+ private static final long SERVICE_CONNECTION_WAIT_TIME_MS = 4 * 1000L; // 4 seconds
+
+ private Context mContext;
+ // The actual LLS service to invoke
+ private ILiveLockScreenManagerProvider mService;
+
+ // Cached change listeners
+ private final RemoteCallbackList<ILiveLockScreenChangeListener> mChangeListeners =
+ new RemoteCallbackList<>();
+
+ private LiveLockScreenInfo mDefaultLlsInfo;
+
+ private final Handler mConnectionHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_TRY_CONNECTING:
+ tryConnecting();
+ break;
+ default:
+ Slog.e(TAG, "Unknown message");
+ }
+ }
+ };
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ Slog.i(TAG, "LiveLockScreenManagerService connected");
+ synchronized (LiveLockScreenServiceBroker.this) {
+ mService = ILiveLockScreenManagerProvider.Stub.asInterface(service);
+ LiveLockScreenServiceBroker.this.notifyAll();
+ // If any change listeners are cached, register them with the newly connected
+ // service.
+ int N = mChangeListeners.getRegisteredCallbackCount();
+ if (mService != null && N > 0) {
+ for (int i = 0; i < N; i++) {
+ try {
+ mService.registerChangeListener(mChangeListeners.getBroadcastItem(i));
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ Slog.i(TAG, "LiveLockScreenManagerService unexpectedly disconnected");
+ synchronized (LiveLockScreenServiceBroker.this) {
+ mService = null;
+ LiveLockScreenServiceBroker.this.notifyAll();
+ }
+ }
+ };
+
+ /**
+ * ILiveLockScreenManager implementation to use when no backing service can be found.
+ */
+ private final ILiveLockScreenManagerProvider mServiceStubForFailure =
+ new ILiveLockScreenManagerProvider() {
+ @Override
+ public void enqueueLiveLockScreen(String pkg, int id, LiveLockScreenInfo lls,
+ int[] idReceived, int userid) throws RemoteException {
+ }
+
+ @Override
+ public void cancelLiveLockScreen(String pkg, int id, int userId) throws RemoteException {
+ }
+
+ @Override
+ public LiveLockScreenInfo getCurrentLiveLockScreen() throws RemoteException {
+ return null;
+ }
+
+ @Override
+ public void updateDefaultLiveLockScreen(LiveLockScreenInfo llsInfo) throws RemoteException {
+
+ }
+
+ @Override
+ public boolean getLiveLockScreenEnabled() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean registerChangeListener(
+ ILiveLockScreenChangeListener listener) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean unregisterChangeListener(
+ ILiveLockScreenChangeListener listener) throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return null;
+ }
+ };
+
+ private final class BinderService extends ILiveLockScreenManager.Stub {
+
+ @Override
+ public void enqueueLiveLockScreen(String pkg, int id,
+ LiveLockScreenInfo lls, int[] idReceived, int userId) throws RemoteException {
+ getServiceGuarded().enqueueLiveLockScreen(pkg, id, lls, idReceived, userId);
+ }
+
+ @Override
+ public void cancelLiveLockScreen(String pkg, int id, int userId) throws RemoteException {
+ getServiceGuarded().cancelLiveLockScreen(pkg, id, userId);
+ }
+
+ @Override
+ public LiveLockScreenInfo getCurrentLiveLockScreen() throws RemoteException {
+ return getServiceGuarded().getCurrentLiveLockScreen();
+ }
+
+ @Override
+ public LiveLockScreenInfo getDefaultLiveLockScreen() throws RemoteException {
+ enforcePrivateAccessPermission();
+ return getDefaultLiveLockScreenInternal();
+ }
+
+ @Override
+ public void setDefaultLiveLockScreen(LiveLockScreenInfo llsInfo) throws RemoteException {
+ enforcePrivateAccessPermission();
+ setDefaultLiveLockScreenInternal(llsInfo);
+ }
+
+ @Override
+ public void setLiveLockScreenEnabled(boolean enabled) throws RemoteException {
+ enforcePrivateAccessPermission();
+ setLiveLockScreenEnabledInternal(enabled);
+ }
+
+ @Override
+ public boolean getLiveLockScreenEnabled() throws RemoteException {
+ return getServiceGuarded().getLiveLockScreenEnabled();
+ }
+
+ @Override
+ public boolean registerChangeListener(
+ ILiveLockScreenChangeListener listener) throws RemoteException {
+ boolean registered = getServiceGuarded().registerChangeListener(listener);
+ if (registered) {
+ mChangeListeners.register(listener);
+ }
+ return getServiceGuarded().registerChangeListener(listener);
+ }
+
+ @Override
+ public boolean unregisterChangeListener(
+ ILiveLockScreenChangeListener listener) throws RemoteException {
+ boolean unregistered = getServiceGuarded().unregisterChangeListener(listener);
+ if (unregistered) {
+ mChangeListeners.unregister(listener);
+ }
+ return unregistered;
+ }
+ }
+
+ public LiveLockScreenServiceBroker(Context context) {
+ super(context);
+ mContext = context;
+ }
+
+ @Override
+ public void onStart() {
+ if (DEBUG) Slog.d(TAG, "service started");
+ if (mContext.getPackageManager().hasSystemFeature(
+ CMContextConstants.Features.LIVE_LOCK_SCREEN)) {
+ publishBinderService(CMContextConstants.CM_LIVE_LOCK_SCREEN_SERVICE,
+ new BinderService());
+ } else {
+ Slog.wtf(TAG, "CM live lock screen service started by system server but feature xml " +
+ "not declared. Not publishing binder service!");
+ }
+ }
+
+ @Override
+ public void onBootPhase(int phase) {
+ if (phase == PHASE_THIRD_PARTY_APPS_CAN_START) {
+ if (DEBUG) Slog.d(TAG, "Third party apps ready");
+
+ // Initialize the default LLS component
+ String defComponent = CMSettings.Secure.getString(mContext.getContentResolver(),
+ CMSettings.Secure.DEFAULT_LIVE_LOCK_SCREEN_COMPONENT);
+ if (defComponent != null) {
+ mDefaultLlsInfo = new LiveLockScreenInfo.Builder()
+ .setComponent(ComponentName.unflattenFromString(defComponent))
+ .build();
+ }
+ // Now that 3rd party apps are ready, try connecting to the backing service
+ tryConnecting();
+ }
+ }
+
+ /**
+ * Binds to the backing service if one is found
+ */
+ private void tryConnecting() {
+ Slog.i(TAG, "Connecting to LiveLockScreenManagerService");
+ synchronized (this) {
+ if (mService != null) {
+ Slog.d(TAG, "Already connected");
+ return;
+ }
+ final Intent intent = new Intent();
+ final ComponentName cn = getLiveLockScreenServiceComponent();
+ if (cn == null) {
+ Slog.e(TAG, "No live lock screen manager service found");
+ return;
+ }
+ intent.setComponent(getLiveLockScreenServiceComponent());
+ try {
+ if (!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+ Slog.e(TAG, "Failed to bind to LiveLockScreenManagerService");
+ }
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Forbidden to bind to LiveLockScreenManagerService", e);
+ }
+ }
+ }
+
+ /**
+ * Queries package manager for the component which handles the
+ * {@link LiveLockScreenManager#SERVICE_INTERFACE} and has been granted the
+ * {@link org.cyanogenmod.platform.internal.Manifest.permission#LIVE_LOCK_SCREEN_MANAGER_PROVIDER}
+ * permission.
+ *
+ * @return A valid component that supports {@link LiveLockScreenManager#SERVICE_INTERFACE} or
+ * null if no component can be found.
+ */
+ @Nullable private ComponentName getLiveLockScreenServiceComponent() {
+ PackageManager pm = mContext.getPackageManager();
+ Intent intent = new Intent(LiveLockScreenManager.SERVICE_INTERFACE);
+ List<ResolveInfo> resolveInfos = pm.queryIntentServices(intent, 0);
+ for (ResolveInfo info : resolveInfos) {
+ if (info != null) {
+ if (pm.checkPermission(Manifest.permission.LIVE_LOCK_SCREEN_MANAGER_PROVIDER,
+ info.serviceInfo.packageName) == PackageManager.PERMISSION_GRANTED &&
+ info.serviceInfo.isEnabled()) {
+ return new ComponentName(info.serviceInfo.packageName, info.serviceInfo.name);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private ILiveLockScreenManagerProvider getOrConnectService() {
+ synchronized (this) {
+ if (mService != null) {
+ return mService;
+ }
+ // Service is not connected. Try blocking connecting.
+ Slog.w(TAG, "LiveLockScreenManagerService not connected. Try connecting...");
+ mConnectionHandler.sendMessage(
+ mConnectionHandler.obtainMessage(MSG_TRY_CONNECTING));
+ final long shouldEnd =
+ SystemClock.elapsedRealtime() + SERVICE_CONNECTION_WAIT_TIME_MS;
+ long waitTime = SERVICE_CONNECTION_WAIT_TIME_MS;
+ while (waitTime > 0) {
+ try {
+ // TODO: consider using Java concurrent construct instead of raw object wait
+ this.wait(waitTime);
+ } catch (InterruptedException e) {
+ Slog.w(TAG, "Connection wait interrupted", e);
+ }
+ if (mService != null) {
+ // Success
+ return mService;
+ }
+ // Calculate remaining waiting time to make sure we wait the full timeout period
+ waitTime = shouldEnd - SystemClock.elapsedRealtime();
+ }
+ // Timed out. Something's really wrong.
+ Slog.e(TAG, "Can not connect to LiveLockScreenManagerService (timed out)");
+ return null;
+ }
+ }
+
+ /**
+ * Make sure to return a non-empty service instance. Return the connected LiveLockScreenManager
+ * instance, if not connected, try connecting. If fail to connect, return a fake service
+ * instance which returns failure to service caller.
+ *
+ * @return a non-empty service instance, real or fake
+ */
+ private ILiveLockScreenManagerProvider getServiceGuarded() {
+ final ILiveLockScreenManagerProvider service = getOrConnectService();
+ if (service != null) {
+ return service;
+ }
+ return mServiceStubForFailure;
+ }
+
+ /**
+ * Enforces the
+ * {@link cyanogenmod.platform.Manifest.permission#LIVE_LOCK_SCREEN_MANAGER_ACCESS_PRIVATE}
+ * permission.
+ */
+ private void enforcePrivateAccessPermission() {
+ mContext.enforceCallingPermission(
+ Manifest.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS_PRIVATE, null);
+ }
+
+ private LiveLockScreenInfo getDefaultLiveLockScreenInternal() {
+ return mDefaultLlsInfo;
+ }
+
+ private void setDefaultLiveLockScreenInternal(LiveLockScreenInfo llsInfo) {
+ if (llsInfo != null && llsInfo.component != null) {
+ // Check that the package this component belongs to has the third party keyguard perm
+ final PackageManager pm = mContext.getPackageManager();
+ final boolean hasThirdPartyKeyguardPermission = pm.checkPermission(
+ Manifest.permission.THIRD_PARTY_KEYGUARD,
+ llsInfo.component.getPackageName()) == PackageManager.PERMISSION_GRANTED;
+ if (!hasThirdPartyKeyguardPermission) {
+ Slog.e(TAG, "Package " + llsInfo.component.getPackageName() +
+ " does not have " + Manifest.permission.THIRD_PARTY_KEYGUARD);
+ return;
+ }
+ }
+
+ long token = Binder.clearCallingIdentity();
+ try {
+ CMSettings.Secure.putString(mContext.getContentResolver(),
+ CMSettings.Secure.DEFAULT_LIVE_LOCK_SCREEN_COMPONENT,
+ (llsInfo != null && llsInfo.component != null)
+ ? llsInfo.component.flattenToString()
+ : "");
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+
+ mDefaultLlsInfo = llsInfo;
+ try {
+ mService.updateDefaultLiveLockScreen(llsInfo);
+ } catch (RemoteException e) {
+ /* ignore */
+ }
+ }
+
+ private void setLiveLockScreenEnabledInternal(boolean enabled) {
+ long token = Binder.clearCallingIdentity();
+ CMSettings.Secure.putInt(mContext.getContentResolver(),
+ CMSettings.Secure.LIVE_LOCK_SCREEN_ENABLED, enabled ? 1 : 0);
+ Binder.restoreCallingIdentity(token);
+ }
+}
diff --git a/cm/res/AndroidManifest.xml b/cm/res/AndroidManifest.xml
index eb4ee81..72c4c70 100644
--- a/cm/res/AndroidManifest.xml
+++ b/cm/res/AndroidManifest.xml
@@ -172,6 +172,26 @@
android:description="@string/permdesc_perfAccessDesc"
android:protectionLevel="signature|privileged" />
+ <!-- Allows an application to access the live lock screen manager. -->
+ <permission android:name="cyanogenmod.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS"
+ android:label="@string/permlab_accessLiveLockScreenService"
+ android:description="@string/permdesc_accessLiveLockScreenService"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows system apps privileged access to the live lock screen manager.
+ @hide -->
+ <permission android:name="cyanogenmod.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS_PRIVATE"
+ android:label="@string/permlab_accessLiveLockScreenServicePrivate"
+ android:description="@string/permdesc_accessLiveLockScreenServicePrivate"
+ android:protectionLevel="signature|privileged" />
+
+ <!-- Permission required to be held for a service that provides a LiveLockScreenManagerService
+ @hide -->
+ <permission android:name="cyanogenmod.permission.LIVE_LOCK_SCREEN_MANAGER_PROVIDER"
+ android:label="@string/permlab_accessLiveLockScreenServiceProvider"
+ android:description="@string/permdesc_accessLiveLockScreenServiceProvider"
+ android:protectionLevel="signature|privileged" />
+
<application android:process="system"
android:persistent="true"
android:hasCode="false"
diff --git a/cm/res/res/values/config.xml b/cm/res/res/values/config.xml
index 4db0133..179ade2 100644
--- a/cm/res/res/values/config.xml
+++ b/cm/res/res/values/config.xml
@@ -89,5 +89,6 @@
<item>org.cyanogenmod.platform.internal.PerformanceManagerService</item>
<item>org.cyanogenmod.platform.internal.ThemeManagerService</item>
<item>org.cyanogenmod.platform.internal.IconCacheManagerService</item>
+ <item>org.cyanogenmod.platform.internal.LiveLockScreenServiceBroker</item>
</string-array>
</resources>
diff --git a/cm/res/res/values/strings.xml b/cm/res/res/values/strings.xml
index 8fd166a..00baff7 100644
--- a/cm/res/res/values/strings.xml
+++ b/cm/res/res/values/strings.xml
@@ -165,4 +165,19 @@
<string name="permlab_perfAccess">access performance manager</string>
<!-- Performance manager permission description -->
<string name="permdesc_perfAccessDesc">Allows an app to access the performance service. Should never be needed for normal apps.</string>
+
+ <!-- Access live lock screen manager service permission label -->
+ <string name="permlab_accessLiveLockScreenService">access live lock screen manager service</string>
+ <!-- Access live lock screen manager service permission description -->
+ <string name="permdesc_accessLiveLockScreenService">Allows an app to access the live lock screen manager service.</string>
+ <!-- Privileged access live lock screen manager service permission label -->
+ <string name="permlab_accessLiveLockScreenServicePrivate">access live lock screen manager service</string>
+ <!-- Privileged access live lock screen manager service permission description -->
+ <string name="permdesc_accessLiveLockScreenServicePrivate">Allows system apps to access the live lock screen manager service.</string>
+ <!-- Live lock screen manager service provider permission label -->
+ <string name="permlab_accessLiveLockScreenServiceProvider">provide live lock screen manager service</string>
+ <!-- Live lock screen manager service provider permission description -->
+ <string name="permdesc_accessLiveLockScreenServiceProvider">Allows a service to provide the live lock screen manager service.</string>
+
+
</resources>
diff --git a/src/java/cyanogenmod/app/BaseLiveLockManagerService.java b/src/java/cyanogenmod/app/BaseLiveLockManagerService.java
new file mode 100644
index 0000000..feae94f
--- /dev/null
+++ b/src/java/cyanogenmod/app/BaseLiveLockManagerService.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cyanogenmod.app;
+
+import android.annotation.NonNull;
+import android.app.AppGlobals;
+import android.app.Service;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.util.Log;
+
+import cyanogenmod.platform.Manifest;
+
+/**
+ * Base Live lock screen manager service to be extended by applications that implement the
+ * {@link LiveLockScreenManager#SERVICE_INTERFACE}
+ *
+ * @hide
+ */
+abstract public class BaseLiveLockManagerService extends Service
+ implements ILiveLockScreenManagerProvider {
+ private static final String TAG = BaseLiveLockManagerService.class.getSimpleName();
+
+ private final RemoteCallbackList<ILiveLockScreenChangeListener> mChangeListeners =
+ new RemoteCallbackList<>();
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return mService;
+ }
+
+ @Override
+ public final IBinder asBinder() {
+ return mService;
+ }
+
+ @Override
+ abstract public void enqueueLiveLockScreen(String pkg, int id, LiveLockScreenInfo lls,
+ int[] idReceived, int userId) throws RemoteException;
+
+ @Override
+ abstract public void cancelLiveLockScreen(String pkg, int id, int userId)
+ throws RemoteException;
+
+ @Override
+ abstract public LiveLockScreenInfo getCurrentLiveLockScreen() throws RemoteException;
+
+ @Override
+ abstract public void updateDefaultLiveLockScreen(LiveLockScreenInfo llsInfo)
+ throws RemoteException;
+
+ @Override
+ public boolean getLiveLockScreenEnabled() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public final boolean registerChangeListener(
+ ILiveLockScreenChangeListener listener) throws RemoteException {
+ return mChangeListeners.register(listener);
+ }
+
+ @Override
+ public final boolean unregisterChangeListener(
+ ILiveLockScreenChangeListener listener) throws RemoteException {
+ return mChangeListeners.unregister(listener);
+ }
+
+ /**
+ * This method should be called whenever there is an update to the current Live lock screen
+ * to be displayed.
+ *
+ * @param llsInfo LiveLockScreenInfo for the current Live lock screen
+ */
+ protected final void notifyChangeListeners(LiveLockScreenInfo llsInfo) {
+ int N = mChangeListeners.beginBroadcast();
+ for (int i = 0; i < N; i++) {
+ ILiveLockScreenChangeListener listener = mChangeListeners.getBroadcastItem(i);
+ try {
+ listener.onLiveLockScreenChanged(llsInfo);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Unable to notifiy change listener", e);
+ }
+ }
+ mChangeListeners.finishBroadcast();
+ }
+
+ /**
+ * Returns true if the caller has been granted the
+ * {@link cyanogenmod.platform.Manifest.permission#LIVE_LOCK_SCREEN_MANAGER_ACCESS_PRIVATE}
+ * permission.
+ *
+ * @return
+ */
+ private final boolean hasPrivatePermissions() {
+ return checkCallingPermission(Manifest.permission
+ .LIVE_LOCK_SCREEN_MANAGER_ACCESS_PRIVATE) == PackageManager.PERMISSION_GRANTED;
+ }
+
+ /**
+ * Enforces the {@link cyanogenmod.platform.Manifest.permission#LIVE_LOCK_SCREEN_MANAGER_ACCESS}
+ * permission.
+ */
+ protected final void enforceAccessPermission() {
+ if (hasPrivatePermissions()) return;
+
+ enforceCallingPermission(Manifest.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS,
+ null);
+ }
+
+ /**
+ * Enforces the
+ * {@link cyanogenmod.platform.Manifest.permission#LIVE_LOCK_SCREEN_MANAGER_ACCESS_PRIVATE}
+ * permission.
+ */
+ protected final void enforcePrivateAccessPermission() {
+ enforceCallingPermission(
+ Manifest.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS_PRIVATE, null);
+ }
+
+ /**
+ * Enforces the LLS being shown/canceled is from the calling package or from a system app that
+ * has the
+ * {@link cyanogenmod.platform.Manifest.permission#LIVE_LOCK_SCREEN_MANAGER_ACCESS_PRIVATE}
+ * permission.
+ *
+ * @param pkg Package name of caller
+ * @param llsInfo Live lock screen info with component to check
+ */
+ protected final void enforceSamePackageOrSystem(String pkg,
+ @NonNull LiveLockScreenInfo llsInfo) {
+ // only apps with the private permission can show/cancel live lock screens from other
+ // packages
+ if (hasPrivatePermissions()) return;
+
+ if (llsInfo.component != null && !llsInfo.component.getPackageName().equals(pkg)) {
+ throw new SecurityException("Modifying Live lock screen from different packages not " +
+ "allowed. Calling package: " + pkg + " LLS package: " +
+ llsInfo.component.getPackageName());
+ }
+
+ final int uid = Binder.getCallingUid();
+ try {
+ ApplicationInfo ai = AppGlobals.getPackageManager().getApplicationInfo(
+ pkg, 0, UserHandle.getCallingUserId());
+ if (ai == null) {
+ throw new SecurityException("Unknown package " + pkg);
+ }
+ if (!UserHandle.isSameApp(ai.uid, uid)) {
+ throw new SecurityException("Calling uid " + uid + " gave package"
+ + pkg + " which is owned by uid " + ai.uid);
+ }
+ } catch (RemoteException re) {
+ throw new SecurityException("Unknown package " + pkg + "\n" + re);
+ }
+ }
+
+ private final IBinder mService = new ILiveLockScreenManagerProvider.Stub() {
+ @Override
+ public void enqueueLiveLockScreen(String pkg, int id, LiveLockScreenInfo llsInfo,
+ int[] idReceived, int userId) throws RemoteException {
+ enforceAccessPermission();
+ enforceSamePackageOrSystem(pkg, llsInfo);
+ BaseLiveLockManagerService.this.enqueueLiveLockScreen(pkg, id, llsInfo, idReceived,
+ userId);
+ }
+
+ @Override
+ public void cancelLiveLockScreen(String pkg, int id, int userId) throws RemoteException {
+ enforceAccessPermission();
+ BaseLiveLockManagerService.this.cancelLiveLockScreen(pkg, id, userId);
+ }
+
+ @Override
+ public LiveLockScreenInfo getCurrentLiveLockScreen() throws RemoteException {
+ enforceAccessPermission();
+ return BaseLiveLockManagerService.this.getCurrentLiveLockScreen();
+ }
+
+ @Override
+ public void updateDefaultLiveLockScreen(LiveLockScreenInfo llsInfo) throws RemoteException {
+ enforcePrivateAccessPermission();
+ BaseLiveLockManagerService.this.updateDefaultLiveLockScreen(llsInfo);
+ }
+
+ @Override
+ public boolean getLiveLockScreenEnabled() throws RemoteException {
+ enforceAccessPermission();
+ return BaseLiveLockManagerService.this.getLiveLockScreenEnabled();
+ }
+
+ @Override
+ public boolean registerChangeListener(
+ ILiveLockScreenChangeListener listener) throws RemoteException {
+ enforcePrivateAccessPermission();
+ return BaseLiveLockManagerService.this.registerChangeListener(listener);
+ }
+
+ @Override
+ public boolean unregisterChangeListener(
+ ILiveLockScreenChangeListener listener) throws RemoteException {
+ enforcePrivateAccessPermission();
+ return BaseLiveLockManagerService.this.unregisterChangeListener(listener);
+ }
+ };
+}
diff --git a/src/java/cyanogenmod/app/CMContextConstants.java b/src/java/cyanogenmod/app/CMContextConstants.java
index e90d8ed..6b2cb23 100644
--- a/src/java/cyanogenmod/app/CMContextConstants.java
+++ b/src/java/cyanogenmod/app/CMContextConstants.java
@@ -115,6 +115,11 @@ public final class CMContextConstants {
public static final String CM_ICON_CACHE_SERVICE = "cmiconcache";
/**
+ * @hide
+ */
+ public static final String CM_LIVE_LOCK_SCREEN_SERVICE = "cmlivelockscreen";
+
+ /**
* Features supported by the CMSDK.
*/
public static class Features {
@@ -181,5 +186,13 @@ public final class CMContextConstants {
*/
@SdkConstant(SdkConstant.SdkConstantType.FEATURE)
public static final String PARTNER = "org.cyanogenmod.partner";
+
+ /*
+ * Feature for {@link PackageManager#getSystemAvailableFeatures} and
+ * {@link PackageManager#hasSystemFeature}: The device includes the Live lock screen
+ * feature.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.FEATURE)
+ public static final String LIVE_LOCK_SCREEN = "org.cyanogenmod.livelockscreen";
}
}
diff --git a/src/java/cyanogenmod/app/ILiveLockScreenChangeListener.aidl b/src/java/cyanogenmod/app/ILiveLockScreenChangeListener.aidl
new file mode 100644
index 0000000..48e7f36
--- /dev/null
+++ b/src/java/cyanogenmod/app/ILiveLockScreenChangeListener.aidl
@@ -0,0 +1,27 @@
+/*
+** Copyright (C) 2016 The CyanogenMod Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package cyanogenmod.app;
+
+import cyanogenmod.app.LiveLockScreenInfo;
+
+/**
+ * Listener interface for notifying clients that the current Live lock screen has changed.
+ * @hide
+ */
+interface ILiveLockScreenChangeListener {
+ void onLiveLockScreenChanged(in LiveLockScreenInfo llsInfo);
+} \ No newline at end of file
diff --git a/src/java/cyanogenmod/app/ILiveLockScreenManager.aidl b/src/java/cyanogenmod/app/ILiveLockScreenManager.aidl
new file mode 100644
index 0000000..15142c1
--- /dev/null
+++ b/src/java/cyanogenmod/app/ILiveLockScreenManager.aidl
@@ -0,0 +1,73 @@
+/*
+** Copyright (C) 2016 The CyanogenMod Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package cyanogenmod.app;
+
+import cyanogenmod.app.ILiveLockScreenChangeListener;
+import cyanogenmod.app.LiveLockScreenInfo;
+
+/** @hide */
+interface ILiveLockScreenManager {
+
+ /**
+ * Enqueue a Live lock screen to be displayed.
+ */
+ void enqueueLiveLockScreen(String pkg, int id, in LiveLockScreenInfo lls,
+ inout int[] idReceived, int userId);
+
+ /**
+ * Cancel displaying a Live lock screen.
+ */
+ void cancelLiveLockScreen(String pkg, int id, int userId);
+
+ /**
+ * Get the current Live lock screen that should be displayed.
+ */
+ LiveLockScreenInfo getCurrentLiveLockScreen();
+
+ /**
+ * Get the default Live lock screen. This is the Live lock screen that should be displayed
+ * when no other Live lock screens are queued.
+ */
+ LiveLockScreenInfo getDefaultLiveLockScreen();
+
+ /**
+ * Set the default Live lock screen. This is the Live lock screen that should be displayed
+ * when no other Live lock screens are queued.
+ */
+ void setDefaultLiveLockScreen(in LiveLockScreenInfo llsInfo);
+
+ /**
+ * Set whether Live lock screen feature is enabled.
+ */
+ oneway void setLiveLockScreenEnabled(boolean enabled);
+
+ /**
+ * Get the enabled state of the Live lock screen feature.
+ */
+ boolean getLiveLockScreenEnabled();
+
+ /**
+ * Registers an ILiveLockScreenChangeListener that will be called when the current Live lock
+ * screen changes.
+ */
+ boolean registerChangeListener(in ILiveLockScreenChangeListener listener);
+
+ /**
+ * Unregisters a previously registered ILiveLockScreenChangeListener.
+ */
+ boolean unregisterChangeListener(in ILiveLockScreenChangeListener listener);
+} \ No newline at end of file
diff --git a/src/java/cyanogenmod/app/ILiveLockScreenManagerProvider.aidl b/src/java/cyanogenmod/app/ILiveLockScreenManagerProvider.aidl
new file mode 100644
index 0000000..933eb97
--- /dev/null
+++ b/src/java/cyanogenmod/app/ILiveLockScreenManagerProvider.aidl
@@ -0,0 +1,65 @@
+/*
+** Copyright (C) 2016 The CyanogenMod Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package cyanogenmod.app;
+
+import cyanogenmod.app.ILiveLockScreenChangeListener;
+import cyanogenmod.app.LiveLockScreenInfo;
+
+/**
+ * Interface to be implemented by services that support the
+ * {@link LiveLockScreenManager#SERVICE_INTERFACE}
+ * @hide
+ */
+interface ILiveLockScreenManagerProvider {
+
+ /**
+ * Enqueue a Live lock screen to be displayed.
+ */
+ void enqueueLiveLockScreen(String pkg, int id, in LiveLockScreenInfo lls,
+ inout int[] idReceived, int userId);
+
+ /**
+ * Cancel displaying a Live lock screen.
+ */
+ void cancelLiveLockScreen(String pkg, int id, int userId);
+
+ /**
+ * Get the current Live lock screen that should be displayed.
+ */
+ LiveLockScreenInfo getCurrentLiveLockScreen();
+
+ /**
+ * Called when the default Live lock screen has changed.
+ */
+ oneway void updateDefaultLiveLockScreen(in LiveLockScreenInfo llsInfo);
+
+ /**
+ * Get the enabled state of the Live lock screen feature.
+ */
+ boolean getLiveLockScreenEnabled();
+
+ /**
+ * Registers an ILiveLockScreenChangeListener that will be called when the current Live lock
+ * screen changes.
+ */
+ boolean registerChangeListener(in ILiveLockScreenChangeListener listener);
+
+ /**
+ * Unregisters a previously registered ILiveLockScreenChangeListener.
+ */
+ boolean unregisterChangeListener(in ILiveLockScreenChangeListener listener);
+} \ No newline at end of file
diff --git a/src/java/cyanogenmod/app/LiveLockScreenInfo.aidl b/src/java/cyanogenmod/app/LiveLockScreenInfo.aidl
new file mode 100644
index 0000000..bffa3b0
--- /dev/null
+++ b/src/java/cyanogenmod/app/LiveLockScreenInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cyanogenmod.app;
+
+parcelable LiveLockScreenInfo;
diff --git a/src/java/cyanogenmod/app/LiveLockScreenInfo.java b/src/java/cyanogenmod/app/LiveLockScreenInfo.java
new file mode 100644
index 0000000..33afd89
--- /dev/null
+++ b/src/java/cyanogenmod/app/LiveLockScreenInfo.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cyanogenmod.app;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import android.text.TextUtils;
+import cyanogenmod.os.Build;
+
+/**
+ * Data structure defining a Live lock screen.
+ */
+public class LiveLockScreenInfo implements Parcelable {
+
+ /**
+ * Default Live lock screen {@link #priority}.
+ */
+ public static final int PRIORITY_DEFAULT = 0;
+
+ /**
+ * Lower {@link #priority}, for items that are less important.
+ */
+ public static final int PRIORITY_LOW = -1;
+
+ /**
+ * Lowest {@link #priority}.
+ */
+ public static final int PRIORITY_MIN = -2;
+
+ /**
+ * Higher {@link #priority}, for items that are more important
+ */
+ public static final int PRIORITY_HIGH = 1;
+
+ /**
+ * Highest {@link #priority}.
+ */
+ public static final int PRIORITY_MAX = 2;
+
+ /**
+ * The component, which implements
+ * {@link cyanogenmod.externalviews.KeyguardExternalViewProviderService}, to display for this
+ * live lock screen.
+ */
+ public ComponentName component;
+
+ /**
+ * Relative priority for this Live lock screen.
+ */
+ public int priority;
+
+ /**
+ * Constructs a LiveLockScreenInfo object with the given values.
+ * You might want to consider using {@link Builder} instead.
+ */
+ public LiveLockScreenInfo(@NonNull ComponentName component, int priority) {
+ this.component = component;
+ this.priority = priority;
+ }
+
+ /**
+ * Constructs a LiveLockScreenInfo object with default values.
+ * You might want to consider using {@link Builder} instead.
+ */
+ public LiveLockScreenInfo()
+ {
+ this.component = null;
+ this.priority = PRIORITY_DEFAULT;
+ }
+
+ private LiveLockScreenInfo(Parcel source) {
+ // Read parcelable version, make sure to define explicit changes
+ // within {@link Build.PARCELABLE_VERSION);
+ int version = source.readInt();
+ int size = source.readInt();
+ int start = source.dataPosition();
+
+ this.priority = source.readInt();
+ String component = source.readString();
+ this.component = !TextUtils.isEmpty(component)
+ ? ComponentName.unflattenFromString(component)
+ : null;
+
+ source.setDataPosition(start + size);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ // Write parcelable version, make sure to define explicit changes
+ // within {@link Build.PARCELABLE_VERSION);
+ dest.writeInt(Build.PARCELABLE_VERSION);
+ int sizePos = dest.dataPosition();
+ // Inject a placeholder that will store the parcel size from this point on
+ // (not including the size itself).
+ dest.writeInt(0);
+ int dataStartPos = dest.dataPosition();
+
+ dest.writeInt(priority);
+ dest.writeString(component != null ? component.flattenToString() : "");
+
+ // Go back and write size
+ int size = dest.dataPosition() - dataStartPos;
+ dest.setDataPosition(sizePos);
+ dest.writeInt(size);
+ dest.setDataPosition(dataStartPos + size);
+ }
+
+ @Override
+ public String toString() {
+ return "LiveLockScreenInfo: priority=" + priority +
+ ", component=" + component;
+ }
+
+ @Override
+ public LiveLockScreenInfo clone() {
+ LiveLockScreenInfo that = new LiveLockScreenInfo();
+ cloneInto(that);
+ return that;
+ }
+
+ /**
+ * Copy all (or if heavy is false, all except Bitmaps and RemoteViews) members
+ * of this into that.
+ * @hide
+ */
+ public void cloneInto(LiveLockScreenInfo that) {
+ that.component = this.component.clone();
+ that.priority = this.priority;
+ }
+
+ public static final Parcelable.Creator<LiveLockScreenInfo> CREATOR =
+ new Parcelable.Creator<LiveLockScreenInfo>() {
+ @Override
+ public LiveLockScreenInfo createFromParcel(Parcel source) {
+ return new LiveLockScreenInfo(source);
+ }
+
+ @Override
+ public LiveLockScreenInfo[] newArray(int size) {
+ return new LiveLockScreenInfo[0];
+ }
+ };
+
+ /**
+ * Builder class for {@link LiveLockScreenInfo} objects. Provides a convenient way to set
+ * various fields of a {@link LiveLockScreenInfo}.
+ */
+ public static class Builder {
+ private int mPriority;
+ private ComponentName mComponent;
+
+ public Builder setPriority(int priority) {
+ if (priority < PRIORITY_MIN || priority > PRIORITY_MAX) {
+ throw new IllegalArgumentException("Invalid priorty given (" + priority + "): " +
+ PRIORITY_MIN + " <= priority <= " + PRIORITY_MIN);
+ }
+ mPriority = priority;
+ return this;
+ }
+
+ public Builder setComponent(@NonNull ComponentName component) {
+ if (component == null) {
+ throw new IllegalArgumentException(
+ "Cannot call setComponent with a null component");
+ }
+ mComponent = component;
+ return this;
+ }
+
+ public LiveLockScreenInfo build() {
+ return new LiveLockScreenInfo(mComponent, mPriority);
+ }
+ }
+}
diff --git a/src/java/cyanogenmod/app/LiveLockScreenManager.java b/src/java/cyanogenmod/app/LiveLockScreenManager.java
new file mode 100644
index 0000000..c5fa4ce
--- /dev/null
+++ b/src/java/cyanogenmod/app/LiveLockScreenManager.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2016 The CyanogenMod Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package cyanogenmod.app;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SdkConstant;
+import android.content.Context;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Log;
+
+/**
+ * Manages enabling/disabling Live lock screens as well as what Live lock screen to display when
+ * enabled.
+ */
+public class LiveLockScreenManager {
+ private static final String TAG = LiveLockScreenManager.class.getSimpleName();
+ private static ILiveLockScreenManager sService;
+ private static LiveLockScreenManager sInstance;
+
+ private Context mContext;
+
+ /**
+ * The {@link android.content.Intent} that must be declared as handled by the service.
+ */
+ @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
+ public static final String SERVICE_INTERFACE
+ = "cyanogenmod.app.LiveLockScreenManagerService";
+
+ private LiveLockScreenManager(Context context) {
+ mContext = context;
+ sService = getService();
+ if (context.getPackageManager().hasSystemFeature(
+ CMContextConstants.Features.LIVE_LOCK_SCREEN) && sService == null) {
+ throw new RuntimeException("Unable to get LiveLockScreenManagerService. " +
+ "The service either crashed, was not started, or the interface has " +
+ "been called to early in SystemServer init");
+ }
+ }
+
+ private ILiveLockScreenManager getService() {
+ if (sService == null) {
+ IBinder b = ServiceManager.getService(CMContextConstants.CM_LIVE_LOCK_SCREEN_SERVICE);
+ if (b != null) {
+ sService = ILiveLockScreenManager.Stub.asInterface(b);
+ }
+ }
+
+ return sService;
+ }
+
+ private void logServiceException(Exception e) {
+ Log.w(TAG, "Unable to access LiveLockScreenServiceBroker", e);
+ }
+
+ public static LiveLockScreenManager getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new LiveLockScreenManager(context);
+ }
+
+ return sInstance;
+ }
+
+ /**
+ * Requests a Live lock screen, defined in {@param lls}, to be displayed with the given id.
+ * @param id An identifier for this notification unique within your application.
+ * @param llsInfo A {@link LiveLockScreenInfo} object describing what Live lock screen to show
+ * the user.
+ * @return True if the Live lock screen was successfully received by the backing service
+ */
+ public boolean show(int id, @NonNull final LiveLockScreenInfo llsInfo) {
+ int[] idOut = new int[1];
+ String pkg = mContext.getPackageName();
+ boolean success = true;
+ try {
+ sService.enqueueLiveLockScreen(pkg, id, llsInfo, idOut, UserHandle.myUserId());
+ if (id != idOut[0]) {
+ Log.w(TAG, "show: id corrupted: sent " + id + ", got back " + idOut[0]);
+ success = false;
+ }
+ } catch (RemoteException e) {
+ logServiceException(e);
+ success = false;
+ }
+
+ return success;
+ }
+
+ /**
+ * Cancels a previously shown Live lock screen.
+ * @param id An identifier for this notification unique within your application.
+ */
+ public void cancel(int id) {
+ String pkg = mContext.getPackageName();
+ try {
+ sService.cancelLiveLockScreen(pkg, id, UserHandle.myUserId());
+ } catch (RemoteException e) {
+ logServiceException(e);
+ }
+ }
+
+ /**
+ * Sets the default Live lock screen to display when no other Live lock screens are queued
+ * up for display.
+ * <p>
+ * This is not available to third party applications.
+ * </p>
+ */
+ public void setDefaultLiveLockScreen(@Nullable LiveLockScreenInfo llsInfo) {
+ try {
+ sService.setDefaultLiveLockScreen(llsInfo);
+ } catch (RemoteException e) {
+ logServiceException(e);
+ }
+ }
+
+ /**
+ * Gets the default Live lock screen that is displayed when no other Live lock screens are
+ * queued up for display.
+ * <p>
+ * This is not available to third party applications.
+ * </p>
+ */
+ public LiveLockScreenInfo getDefaultLiveLockScreen() {
+ try {
+ return sService.getDefaultLiveLockScreen();
+ } catch (RemoteException e) {
+ logServiceException(e);
+ }
+
+ return null;
+ }
+
+ /** @hide */
+ public LiveLockScreenInfo getCurrentLiveLockScreen() {
+ LiveLockScreenInfo lls = null;
+ try {
+ lls = sService.getCurrentLiveLockScreen();
+ } catch (RemoteException e) {
+ logServiceException(e);
+ }
+
+ return lls;
+ }
+
+ /** @hide */
+ public boolean getLiveLockScreenEnabled() {
+ try {
+ return sService.getLiveLockScreenEnabled();
+ } catch (RemoteException e) {
+ logServiceException(e);
+ }
+
+ return false;
+ }
+
+ /** @hide */
+ public void setLiveLockScreenEnabled(boolean enabled) {
+ try {
+ sService.setLiveLockScreenEnabled(enabled);
+ } catch (RemoteException e) {
+ logServiceException(e);
+ }
+ }
+}
diff --git a/src/java/cyanogenmod/providers/CMSettings.java b/src/java/cyanogenmod/providers/CMSettings.java
index 9859415..8d76bb0 100644
--- a/src/java/cyanogenmod/providers/CMSettings.java
+++ b/src/java/cyanogenmod/providers/CMSettings.java
@@ -2649,6 +2649,13 @@ public final class CMSettings {
public static final String LIVE_LOCK_SCREEN_ENABLED = "live_lock_screen_enabled";
/**
+ * The user selected Live lock screen to display
+ * @hide
+ */
+ public static final String DEFAULT_LIVE_LOCK_SCREEN_COMPONENT =
+ "default_live_lock_screen_component";
+
+ /**
* Whether keyguard will direct show security view (0 = false, 1 = true)
* @hide
*/
diff --git a/system-api/cm_system-current.txt b/system-api/cm_system-current.txt
index e906245..a8b7319 100644
--- a/system-api/cm_system-current.txt
+++ b/system-api/cm_system-current.txt
@@ -196,6 +196,38 @@ package cyanogenmod.app {
field public static final java.lang.String SERVICE_INTERFACE = "cyanogenmod.app.CustomTileListenerService";
}
+ public class LiveLockScreenInfo implements android.os.Parcelable {
+ ctor public LiveLockScreenInfo(android.content.ComponentName, int);
+ ctor public LiveLockScreenInfo();
+ method public cyanogenmod.app.LiveLockScreenInfo clone();
+ method public int describeContents();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<cyanogenmod.app.LiveLockScreenInfo> CREATOR;
+ field public static final int PRIORITY_DEFAULT = 0; // 0x0
+ field public static final int PRIORITY_HIGH = 1; // 0x1
+ field public static final int PRIORITY_LOW = -1; // 0xffffffff
+ field public static final int PRIORITY_MAX = 2; // 0x2
+ field public static final int PRIORITY_MIN = -2; // 0xfffffffe
+ field public android.content.ComponentName component;
+ field public int priority;
+ }
+
+ public static class LiveLockScreenInfo.Builder {
+ ctor public LiveLockScreenInfo.Builder();
+ method public cyanogenmod.app.LiveLockScreenInfo build();
+ method public cyanogenmod.app.LiveLockScreenInfo.Builder setComponent(android.content.ComponentName);
+ method public cyanogenmod.app.LiveLockScreenInfo.Builder setPriority(int);
+ }
+
+ public class LiveLockScreenManager {
+ method public void cancel(int);
+ method public cyanogenmod.app.LiveLockScreenInfo getDefaultLiveLockScreen();
+ method public static cyanogenmod.app.LiveLockScreenManager getInstance(android.content.Context);
+ method public void setDefaultLiveLockScreen(cyanogenmod.app.LiveLockScreenInfo);
+ method public boolean show(int, cyanogenmod.app.LiveLockScreenInfo);
+ field public static final java.lang.String SERVICE_INTERFACE = "cyanogenmod.app.LiveLockScreenManagerService";
+ }
+
public class PartnerInterface {
method public java.lang.String getCurrentHotwordPackageName();
method public static cyanogenmod.app.PartnerInterface getInstance(android.content.Context);
@@ -564,6 +596,7 @@ package cyanogenmod.platform {
field public static final java.lang.String ACCESS_APP_SUGGESTIONS = "cyanogenmod.permission.ACCESS_APP_SUGGESTIONS";
field public static final java.lang.String ACCESS_THEME_MANAGER = "cyanogenmod.permission.ACCESS_THEME_MANAGER";
field public static final java.lang.String HARDWARE_ABSTRACTION_ACCESS = "cyanogenmod.permission.HARDWARE_ABSTRACTION_ACCESS";
+ field public static final java.lang.String LIVE_LOCK_SCREEN_MANAGER_ACCESS = "cyanogenmod.permission.LIVE_LOCK_SCREEN_MANAGER_ACCESS";
field public static final java.lang.String MANAGE_ALARMS = "cyanogenmod.permission.MANAGE_ALARMS";
field public static final java.lang.String MANAGE_PERSISTENT_STORAGE = "cyanogenmod.permission.MANAGE_PERSISTENT_STORAGE";
field public static final java.lang.String MODIFY_MSIM_PHONE_STATE = "cyanogenmod.permission.MODIFY_MSIM_PHONE_STATE";