From bfa500dd1551b319320bd625680b77b702b6b771 Mon Sep 17 00:00:00 2001 From: d34d Date: Tue, 8 Mar 2016 09:14:34 -0800 Subject: [PATCH] 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 --- api/cm_current.txt | 33 ++ .../internal/LiveLockScreenServiceBroker.java | 415 ++++++++++++++++++ cm/res/AndroidManifest.xml | 20 + cm/res/res/values/config.xml | 1 + cm/res/res/values/strings.xml | 15 + .../app/BaseLiveLockManagerService.java | 226 ++++++++++ .../cyanogenmod/app/CMContextConstants.java | 13 + .../app/ILiveLockScreenChangeListener.aidl | 27 ++ .../app/ILiveLockScreenManager.aidl | 73 +++ .../app/ILiveLockScreenManagerProvider.aidl | 65 +++ .../cyanogenmod/app/LiveLockScreenInfo.aidl | 19 + .../cyanogenmod/app/LiveLockScreenInfo.java | 196 +++++++++ .../app/LiveLockScreenManager.java | 182 ++++++++ .../cyanogenmod/providers/CMSettings.java | 7 + system-api/cm_system-current.txt | 33 ++ 15 files changed, 1325 insertions(+) create mode 100644 cm/lib/main/java/org/cyanogenmod/platform/internal/LiveLockScreenServiceBroker.java create mode 100644 src/java/cyanogenmod/app/BaseLiveLockManagerService.java create mode 100644 src/java/cyanogenmod/app/ILiveLockScreenChangeListener.aidl create mode 100644 src/java/cyanogenmod/app/ILiveLockScreenManager.aidl create mode 100644 src/java/cyanogenmod/app/ILiveLockScreenManagerProvider.aidl create mode 100644 src/java/cyanogenmod/app/LiveLockScreenInfo.aidl create mode 100644 src/java/cyanogenmod/app/LiveLockScreenInfo.java create mode 100644 src/java/cyanogenmod/app/LiveLockScreenManager.java 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 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 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 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" /> + + + + + + + + + org.cyanogenmod.platform.internal.PerformanceManagerService org.cyanogenmod.platform.internal.ThemeManagerService org.cyanogenmod.platform.internal.IconCacheManagerService + org.cyanogenmod.platform.internal.LiveLockScreenServiceBroker 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 @@ access performance manager Allows an app to access the performance service. Should never be needed for normal apps. + + + access live lock screen manager service + + Allows an app to access the live lock screen manager service. + + access live lock screen manager service + + Allows system apps to access the live lock screen manager service. + + provide live lock screen manager service + + Allows a service to provide the live lock screen manager service. + + 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 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 @@ -114,6 +114,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. */ @@ -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 CREATOR = + new Parcelable.Creator() { + @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. + *

+ * This is not available to third party applications. + *

+ */ + 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. + *

+ * This is not available to third party applications. + *

+ */ + 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 @@ -2648,6 +2648,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 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";