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
This commit is contained in:
d34d 2016-03-08 09:14:34 -08:00 committed by Clark Scheff
parent 11334c9592
commit bfa500dd15
15 changed files with 1325 additions and 0 deletions

View File

@ -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";

View File

@ -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);
}
}

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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);
}
};
}

View File

@ -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";
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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

View File

@ -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";