Merge "Implement the launcher side permission." into nyc-dev
This commit is contained in:
@@ -9454,6 +9454,7 @@ package android.content.pm {
|
||||
method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
|
||||
method public boolean hasShortcutHostPermission();
|
||||
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
|
||||
method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle);
|
||||
method public void pinShortcuts(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
|
||||
|
||||
@@ -9788,6 +9788,7 @@ package android.content.pm {
|
||||
method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
|
||||
method public boolean hasShortcutHostPermission();
|
||||
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
|
||||
method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle);
|
||||
method public void pinShortcuts(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
|
||||
|
||||
@@ -9463,6 +9463,7 @@ package android.content.pm {
|
||||
method public int getShortcutIconResId(android.content.pm.ShortcutInfo, android.os.UserHandle);
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getShortcutInfo(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
|
||||
method public java.util.List<android.content.pm.ShortcutInfo> getShortcuts(android.content.pm.LauncherApps.ShortcutQuery, android.os.UserHandle);
|
||||
method public boolean hasShortcutHostPermission();
|
||||
method public boolean isActivityEnabled(android.content.ComponentName, android.os.UserHandle);
|
||||
method public boolean isPackageEnabled(java.lang.String, android.os.UserHandle);
|
||||
method public void pinShortcuts(java.lang.String, java.util.List<java.lang.String>, android.os.UserHandle);
|
||||
|
||||
@@ -58,4 +58,6 @@ interface ILauncherApps {
|
||||
int getShortcutIconResId(String callingPackage, in ShortcutInfo shortcut, in UserHandle user);
|
||||
ParcelFileDescriptor getShortcutIconFd(String callingPackage, in ShortcutInfo shortcut,
|
||||
in UserHandle user);
|
||||
|
||||
boolean hasShortcutHostPermission(String callingPackage);
|
||||
}
|
||||
|
||||
@@ -16,11 +16,9 @@
|
||||
|
||||
package android.content.pm;
|
||||
|
||||
import android.Manifest.permission;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
@@ -30,7 +28,6 @@ import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.Parcel;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
@@ -159,6 +156,9 @@ public class LauncherApps {
|
||||
* Indicates that one or more shortcuts (which may be dynamic and/or pinned)
|
||||
* have been added, updated or removed.
|
||||
*
|
||||
* <p>Only the applications that are allowed to access the shortcut information,
|
||||
* as defined in {@link #hasShortcutHostPermission()}, will receive it.
|
||||
*
|
||||
* @param packageName The name of the package that has the shortcuts.
|
||||
* @param shortcuts all shortcuts from the package (dynamic and/or pinned).
|
||||
* @param user The UserHandle of the profile that generated the change.
|
||||
@@ -394,17 +394,35 @@ public class LauncherApps {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the caller can access the shortcut information.
|
||||
*
|
||||
* <p>Only the default launcher can access the shortcut information.
|
||||
*
|
||||
* <p>Note when this method returns {@code false}, that may be a temporary situation because
|
||||
* the user is trying a new launcher application. The user may decide to change the default
|
||||
* launcher to the calling application again, so even if a launcher application loses
|
||||
* this permission, it does <b>not</b> have to purge pinned shortcut information.
|
||||
*/
|
||||
public boolean hasShortcutHostPermission() {
|
||||
try {
|
||||
return mService.hasShortcutHostPermission(mContext.getPackageName());
|
||||
} catch (RemoteException re) {
|
||||
throw re.rethrowFromSystemServer();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the IDs of {@link ShortcutInfo}s that match {@code query}.
|
||||
*
|
||||
* <p>Callers mut have the {@link permission#BIND_APPWIDGET} permission.
|
||||
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
|
||||
* #hasShortcutHostPermission()}.
|
||||
*
|
||||
* @param query result includes shortcuts matching this query.
|
||||
* @param user The UserHandle of the profile.
|
||||
*
|
||||
* @return the IDs of {@link ShortcutInfo}s that match the query.
|
||||
*/
|
||||
@RequiresPermission(permission.BIND_APPWIDGET)
|
||||
@Nullable
|
||||
public List<ShortcutInfo> getShortcuts(@NonNull ShortcutQuery query,
|
||||
@NonNull UserHandle user) {
|
||||
@@ -420,7 +438,8 @@ public class LauncherApps {
|
||||
/**
|
||||
* Returns {@link ShortcutInfo}s with the given IDs from a package.
|
||||
*
|
||||
* <p>Callers mut have the {@link permission#BIND_APPWIDGET} permission.
|
||||
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
|
||||
* #hasShortcutHostPermission()}.
|
||||
*
|
||||
* @param packageName The target package.
|
||||
* @param ids IDs of the shortcuts to retrieve.
|
||||
@@ -428,7 +447,6 @@ public class LauncherApps {
|
||||
*
|
||||
* @return list of {@link ShortcutInfo} associated with the package.
|
||||
*/
|
||||
@RequiresPermission(permission.BIND_APPWIDGET)
|
||||
@Nullable
|
||||
public List<ShortcutInfo> getShortcutInfo(@NonNull String packageName,
|
||||
@NonNull List<String> ids, @NonNull UserHandle user) {
|
||||
@@ -447,13 +465,13 @@ public class LauncherApps {
|
||||
* <p>This API is <b>NOT</b> cumulative; this will replace all pinned shortcuts for the package.
|
||||
* However, different launchers may have different set of pinned shortcuts.
|
||||
*
|
||||
* <p>Callers must have the {@link permission#BIND_APPWIDGET} permission.
|
||||
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
|
||||
* #hasShortcutHostPermission()}.
|
||||
*
|
||||
* @param packageName The target package name.
|
||||
* @param shortcutIds The IDs of the shortcut to be pinned.
|
||||
* @param user The UserHandle of the profile.
|
||||
*/
|
||||
@RequiresPermission(permission.BIND_APPWIDGET)
|
||||
public void pinShortcuts(@NonNull String packageName, @NonNull List<String> shortcutIds,
|
||||
@NonNull UserHandle user) {
|
||||
try {
|
||||
@@ -467,12 +485,12 @@ public class LauncherApps {
|
||||
* Return the icon resource ID, if {@code shortcut} has one
|
||||
* (i.e. when {@link ShortcutInfo#hasIconResource()} returns {@code true}).
|
||||
*
|
||||
* <p>Callers mut have the {@link permission#BIND_APPWIDGET} permission.
|
||||
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
|
||||
* #hasShortcutHostPermission()}.
|
||||
*
|
||||
* @param shortcut The target shortcut.
|
||||
* @param user The UserHandle of the profile.
|
||||
*/
|
||||
@RequiresPermission(permission.BIND_APPWIDGET)
|
||||
public int getShortcutIconResId(@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
|
||||
try {
|
||||
return mService.getShortcutIconResId(mContext.getPackageName(), shortcut, user);
|
||||
@@ -485,12 +503,12 @@ public class LauncherApps {
|
||||
* Return the icon as {@link ParcelFileDescriptor}, when it's stored as a file
|
||||
* (i.e. when {@link ShortcutInfo#hasIconFile()} returns {@code true}).
|
||||
*
|
||||
* <p>Callers mut have the {@link permission#BIND_APPWIDGET} permission.
|
||||
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
|
||||
* #hasShortcutHostPermission()}.
|
||||
*
|
||||
* @param shortcut The target shortcut.
|
||||
* @param user The UserHandle of the profile.
|
||||
*/
|
||||
@RequiresPermission(permission.BIND_APPWIDGET)
|
||||
public ParcelFileDescriptor getShortcutIconFd(
|
||||
@NonNull ShortcutInfo shortcut, @NonNull UserHandle user) {
|
||||
try {
|
||||
@@ -503,7 +521,8 @@ public class LauncherApps {
|
||||
/**
|
||||
* Launches a shortcut.
|
||||
*
|
||||
* <p>Callers mut have the {@link permission#BIND_APPWIDGET} permission.
|
||||
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
|
||||
* #hasShortcutHostPermission()}.
|
||||
*
|
||||
* @param packageName The target shortcut package name.
|
||||
* @param shortcutId The target shortcut ID.
|
||||
@@ -513,7 +532,6 @@ public class LauncherApps {
|
||||
* @return {@code false} when the shortcut is no longer valid (e.g. the creator application
|
||||
* has been uninstalled). {@code true} when the shortcut is still valid.
|
||||
*/
|
||||
@RequiresPermission(permission.BIND_APPWIDGET)
|
||||
public boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
|
||||
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
|
||||
@NonNull UserHandle user) {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.content.pm;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
|
||||
import java.util.List;
|
||||
@@ -140,4 +141,10 @@ public abstract class PackageManagerInternal {
|
||||
* found on the system.
|
||||
*/
|
||||
public abstract ApplicationInfo getApplicationInfo(String packageName, int userId);
|
||||
|
||||
/**
|
||||
* Interface to {@link com.android.server.pm.PackageManagerService#getHomeActivitiesAsUser}.
|
||||
*/
|
||||
public abstract ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
|
||||
int userId);
|
||||
}
|
||||
|
||||
@@ -63,4 +63,6 @@ public abstract class ShortcutServiceInternal {
|
||||
|
||||
public abstract ParcelFileDescriptor getShortcutIconFd(@NonNull String callingPackage,
|
||||
@NonNull ShortcutInfo shortcut, int userId);
|
||||
|
||||
public abstract boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId);
|
||||
}
|
||||
|
||||
@@ -99,6 +99,15 @@ public class LauncherAppsService extends SystemService {
|
||||
mShortcutServiceInternal.addListener(mPackageMonitor);
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
int injectBinderCallingUid() {
|
||||
return getCallingUid();
|
||||
}
|
||||
|
||||
private int getCallingUserId() {
|
||||
return UserHandle.getUserId(injectBinderCallingUid());
|
||||
}
|
||||
|
||||
/*
|
||||
* @see android.content.pm.ILauncherApps#addOnAppsChangedListener(
|
||||
* android.content.pm.IOnAppsChangedListener)
|
||||
@@ -296,17 +305,21 @@ public class LauncherAppsService extends SystemService {
|
||||
}
|
||||
}
|
||||
|
||||
private void enforceShortcutPermission(UserHandle user) {
|
||||
private void ensureShortcutPermission(@NonNull String callingPackage, UserHandle user) {
|
||||
verifyCallingPackage(callingPackage);
|
||||
ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
|
||||
// STOPSHIP Implement it
|
||||
|
||||
if (!mShortcutServiceInternal.hasShortcutHostPermission(callingPackage,
|
||||
user.getIdentifier())) {
|
||||
throw new SecurityException("Caller can't access shortcut information");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ParceledListSlice getShortcuts(String callingPackage, long changedSince,
|
||||
String packageName, ComponentName componentName, int flags, UserHandle user)
|
||||
throws RemoteException {
|
||||
enforceShortcutPermission(user);
|
||||
verifyCallingPackage(callingPackage);
|
||||
ensureShortcutPermission(callingPackage, user);
|
||||
|
||||
return new ParceledListSlice<>(
|
||||
mShortcutServiceInternal.getShortcuts(callingPackage, changedSince, packageName,
|
||||
@@ -316,8 +329,7 @@ public class LauncherAppsService extends SystemService {
|
||||
@Override
|
||||
public ParceledListSlice getShortcutInfo(String callingPackage, String packageName,
|
||||
List<String> ids, UserHandle user) throws RemoteException {
|
||||
enforceShortcutPermission(user);
|
||||
verifyCallingPackage(callingPackage);
|
||||
ensureShortcutPermission(callingPackage, user);
|
||||
|
||||
return new ParceledListSlice<>(
|
||||
mShortcutServiceInternal.getShortcutInfo(callingPackage, packageName,
|
||||
@@ -327,8 +339,7 @@ public class LauncherAppsService extends SystemService {
|
||||
@Override
|
||||
public void pinShortcuts(String callingPackage, String packageName, List<String> ids,
|
||||
UserHandle user) throws RemoteException {
|
||||
enforceShortcutPermission(user);
|
||||
verifyCallingPackage(callingPackage);
|
||||
ensureShortcutPermission(callingPackage, user);
|
||||
|
||||
mShortcutServiceInternal.pinShortcuts(callingPackage, packageName,
|
||||
ids, user.getIdentifier());
|
||||
@@ -337,8 +348,7 @@ public class LauncherAppsService extends SystemService {
|
||||
@Override
|
||||
public int getShortcutIconResId(String callingPackage, ShortcutInfo shortcut,
|
||||
UserHandle user) {
|
||||
enforceShortcutPermission(user);
|
||||
verifyCallingPackage(callingPackage);
|
||||
ensureShortcutPermission(callingPackage, user);
|
||||
|
||||
return mShortcutServiceInternal.getShortcutIconResId(callingPackage, shortcut,
|
||||
user.getIdentifier());
|
||||
@@ -347,19 +357,24 @@ public class LauncherAppsService extends SystemService {
|
||||
@Override
|
||||
public ParcelFileDescriptor getShortcutIconFd(String callingPackage, ShortcutInfo shortcut,
|
||||
UserHandle user) {
|
||||
enforceShortcutPermission(user);
|
||||
verifyCallingPackage(callingPackage);
|
||||
ensureShortcutPermission(callingPackage, user);
|
||||
|
||||
return mShortcutServiceInternal.getShortcutIconFd(callingPackage, shortcut,
|
||||
user.getIdentifier());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasShortcutHostPermission(String callingPackage) throws RemoteException {
|
||||
verifyCallingPackage(callingPackage);
|
||||
return mShortcutServiceInternal.hasShortcutHostPermission(callingPackage,
|
||||
getCallingUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
|
||||
Rect sourceBounds, Bundle startActivityOptions, UserHandle user)
|
||||
throws RemoteException {
|
||||
enforceShortcutPermission(user);
|
||||
verifyCallingPackage(callingPackage);
|
||||
ensureShortcutPermission(callingPackage, user);
|
||||
|
||||
final Intent intent = mShortcutServiceInternal.createShortcutIntent(callingPackage,
|
||||
packageName, shortcutId, user.getIdentifier());
|
||||
@@ -656,6 +671,9 @@ public class LauncherAppsService extends SystemService {
|
||||
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
|
||||
UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
|
||||
if (!isEnabledProfileOf(user, listeningUser, "onShortcutChanged")) continue;
|
||||
|
||||
// STOPSHIP Skip if the receiver doesn't have the permission.
|
||||
|
||||
try {
|
||||
listener.onShortcutChanged(user, packageName,
|
||||
new ParceledListSlice<>(shortcuts));
|
||||
|
||||
@@ -16456,14 +16456,17 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
|
||||
|
||||
@Override
|
||||
public ComponentName getHomeActivities(List<ResolveInfo> allHomeCandidates) {
|
||||
return getHomeActivitiesAsUser(allHomeCandidates, UserHandle.getCallingUserId());
|
||||
}
|
||||
|
||||
ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
|
||||
int userId) {
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN);
|
||||
intent.addCategory(Intent.CATEGORY_HOME);
|
||||
|
||||
final int callingUserId = UserHandle.getCallingUserId();
|
||||
List<ResolveInfo> list = queryIntentActivitiesInternal(intent, null,
|
||||
PackageManager.GET_META_DATA, callingUserId);
|
||||
PackageManager.GET_META_DATA, userId);
|
||||
ResolveInfo preferred = findPreferredActivity(intent, null, 0, list, 0,
|
||||
true, false, false, callingUserId);
|
||||
true, false, false, userId);
|
||||
|
||||
allHomeCandidates.clear();
|
||||
if (list != null) {
|
||||
@@ -19252,6 +19255,12 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
|
||||
public ApplicationInfo getApplicationInfo(String packageName, int userId) {
|
||||
return PackageManagerService.this.getApplicationInfo(packageName, 0 /*flags*/, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ComponentName getHomeActivitiesAsUser(List<ResolveInfo> allHomeCandidates,
|
||||
int userId) {
|
||||
return PackageManagerService.this.getHomeActivitiesAsUser(allHomeCandidates, userId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -28,7 +28,9 @@ import android.content.pm.LauncherApps;
|
||||
import android.content.pm.LauncherApps.ShortcutQuery;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutServiceInternal;
|
||||
import android.content.pm.ShortcutServiceInternal.ShortcutChangeListener;
|
||||
@@ -71,6 +73,7 @@ import com.android.server.LocalServices;
|
||||
import com.android.server.SystemService;
|
||||
|
||||
import libcore.io.IoUtils;
|
||||
import libcore.util.Objects;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlPullParserException;
|
||||
@@ -88,7 +91,6 @@ import java.net.URISyntaxException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
@@ -112,7 +114,7 @@ import java.util.function.Predicate;
|
||||
public class ShortcutService extends IShortcutService.Stub {
|
||||
static final String TAG = "ShortcutService";
|
||||
|
||||
static final boolean DEBUG = false; // STOPSHIP if true
|
||||
static final boolean DEBUG = true; // STOPSHIP if true
|
||||
static final boolean DEBUG_LOAD = false; // STOPSHIP if true
|
||||
|
||||
@VisibleForTesting
|
||||
@@ -156,6 +158,7 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
static final String TAG_INTENT_EXTRAS = "intent-extras";
|
||||
static final String TAG_EXTRAS = "extras";
|
||||
static final String TAG_SHORTCUT = "shortcut";
|
||||
static final String TAG_LAUNCHER = "launcher";
|
||||
|
||||
static final String ATTR_VALUE = "value";
|
||||
static final String ATTR_NAME = "name";
|
||||
@@ -251,10 +254,13 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
private CompressFormat mIconPersistFormat;
|
||||
private int mIconPersistQuality;
|
||||
|
||||
private final PackageManagerInternal mPackageManagerInternal;
|
||||
|
||||
public ShortcutService(Context context) {
|
||||
mContext = Preconditions.checkNotNull(context);
|
||||
LocalServices.addService(ShortcutServiceInternal.class, new LocalService());
|
||||
mHandler = new Handler(BackgroundThread.get().getLooper());
|
||||
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -460,6 +466,11 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
writeTagValue(out, tag, Long.toString(value));
|
||||
}
|
||||
|
||||
static void writeTagValue(XmlSerializer out, String tag, ComponentName name) throws IOException {
|
||||
if (name == null) return;
|
||||
writeTagValue(out, tag, name.flattenToString());
|
||||
}
|
||||
|
||||
static void writeTagExtra(XmlSerializer out, String tag, PersistableBundle bundle)
|
||||
throws IOException, XmlPullParserException {
|
||||
if (bundle == null) return;
|
||||
@@ -658,7 +669,7 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
}
|
||||
|
||||
// TODO Actually make it async.
|
||||
private void scheduleSaveUser(@UserIdInt int userId) {
|
||||
void scheduleSaveUser(@UserIdInt int userId) {
|
||||
synchronized (mLock) {
|
||||
saveUserLocked(userId);
|
||||
}
|
||||
@@ -1289,6 +1300,87 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
Slog.i(TAG, "ShortcutManager: throttling counter reset");
|
||||
}
|
||||
|
||||
// We override this method in unit tests to do a simpler check.
|
||||
boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
|
||||
return hasShortcutHostPermissionInner(callingPackage, userId);
|
||||
}
|
||||
|
||||
// This method is extracted so we can directly call this method from unit tests,
|
||||
// even when hasShortcutPermission() is overridden.
|
||||
@VisibleForTesting
|
||||
boolean hasShortcutHostPermissionInner(@NonNull String callingPackage, int userId) {
|
||||
synchronized (mLock) {
|
||||
long start = 0;
|
||||
if (DEBUG) {
|
||||
start = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
final UserShortcuts user = getUserShortcutsLocked(userId);
|
||||
|
||||
final List<ResolveInfo> allHomeCandidates = new ArrayList<>();
|
||||
|
||||
// Default launcher from package manager.
|
||||
final ComponentName defaultLauncher = injectPackageManagerInternal()
|
||||
.getHomeActivitiesAsUser(allHomeCandidates, userId);
|
||||
|
||||
ComponentName detected;
|
||||
if (defaultLauncher != null) {
|
||||
detected = defaultLauncher;
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Default launcher from PM: " + detected);
|
||||
}
|
||||
} else {
|
||||
detected = user.getLauncherComponent();
|
||||
|
||||
// TODO: Make sure it's still enabled.
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Cached launcher: " + detected);
|
||||
}
|
||||
}
|
||||
|
||||
if (detected == null) {
|
||||
// If we reach here, that means it's the first check since the user was created,
|
||||
// and there's already multiple launchers and there's no default set.
|
||||
// Find the system one with the highest priority.
|
||||
// (We need to check the priority too because of FallbackHome in Settings.)
|
||||
// If there's no system launcher yet, then no one can access shortcuts, until
|
||||
// the user explicitly
|
||||
final int size = allHomeCandidates.size();
|
||||
|
||||
int lastPriority = Integer.MIN_VALUE;
|
||||
for (int i = 0; i < size; i++) {
|
||||
final ResolveInfo ri = allHomeCandidates.get(i);
|
||||
if (!ri.activityInfo.applicationInfo.isSystemApp()) {
|
||||
continue;
|
||||
}
|
||||
if (DEBUG) {
|
||||
Slog.d(TAG, String.format("hasShortcutPermissionInner: pkg=%s prio=%d",
|
||||
ri.activityInfo.getComponentName(), ri.priority));
|
||||
}
|
||||
if (ri.priority < lastPriority) {
|
||||
continue;
|
||||
}
|
||||
detected = ri.activityInfo.getComponentName();
|
||||
lastPriority = ri.priority;
|
||||
}
|
||||
}
|
||||
if (DEBUG) {
|
||||
long end = System.currentTimeMillis();
|
||||
Slog.v(TAG, String.format("hasShortcutPermission took %d ms", end - start));
|
||||
}
|
||||
if (detected != null) {
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "Detected launcher: " + detected);
|
||||
}
|
||||
user.setLauncherComponent(this, detected);
|
||||
return detected.getPackageName().equals(callingPackage);
|
||||
} else {
|
||||
// Default launcher not found.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry point from {@link LauncherApps}.
|
||||
*/
|
||||
@@ -1434,6 +1526,11 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
|
||||
return ShortcutService.this.hasShortcutHostPermission(callingPackage, userId);
|
||||
}
|
||||
}
|
||||
|
||||
// === Dump ===
|
||||
@@ -1512,37 +1609,74 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
(new MyShellCommand()).exec(this, in, out, err, args, resultReceiver);
|
||||
}
|
||||
|
||||
static class CommandException extends Exception {
|
||||
public CommandException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle "adb shell cmd".
|
||||
*/
|
||||
private class MyShellCommand extends ShellCommand {
|
||||
|
||||
private int mUserId = UserHandle.USER_SYSTEM;
|
||||
|
||||
private void parseOptions(boolean takeUser)
|
||||
throws CommandException {
|
||||
String opt;
|
||||
while ((opt = getNextOption()) != null) {
|
||||
switch (opt) {
|
||||
case "--user":
|
||||
if (takeUser) {
|
||||
mUserId = UserHandle.parseUserArg(getNextArgRequired());
|
||||
break;
|
||||
}
|
||||
// fallthrough
|
||||
default:
|
||||
throw new CommandException("Unknown option: " + opt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onCommand(String cmd) {
|
||||
if (cmd == null) {
|
||||
return handleDefaultCommands(cmd);
|
||||
}
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
int ret = 1;
|
||||
switch (cmd) {
|
||||
case "reset-package-throttling":
|
||||
ret = handleResetPackageThrottling();
|
||||
break;
|
||||
case "reset-throttling":
|
||||
ret = handleResetThrottling();
|
||||
break;
|
||||
case "override-config":
|
||||
ret = handleOverrideConfig();
|
||||
break;
|
||||
case "reset-config":
|
||||
ret = handleResetConfig();
|
||||
break;
|
||||
default:
|
||||
return handleDefaultCommands(cmd);
|
||||
try {
|
||||
switch (cmd) {
|
||||
case "reset-package-throttling":
|
||||
handleResetPackageThrottling();
|
||||
break;
|
||||
case "reset-throttling":
|
||||
handleResetThrottling();
|
||||
break;
|
||||
case "override-config":
|
||||
handleOverrideConfig();
|
||||
break;
|
||||
case "reset-config":
|
||||
handleResetConfig();
|
||||
break;
|
||||
case "clear-default-launcher":
|
||||
handleClearDefaultLauncher();
|
||||
break;
|
||||
case "get-default-launcher":
|
||||
handleGetDefaultLauncher();
|
||||
break;
|
||||
case "refresh-default-launcher":
|
||||
handleRefreshDefaultLauncher();
|
||||
break;
|
||||
default:
|
||||
return handleDefaultCommands(cmd);
|
||||
}
|
||||
} catch (CommandException e) {
|
||||
pw.println("Error: " + e.getMessage());
|
||||
return 1;
|
||||
}
|
||||
if (ret == 0) {
|
||||
pw.println("Success");
|
||||
}
|
||||
return ret;
|
||||
pw.println("Success");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1562,6 +1696,15 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
pw.println("cmd shortcut reset-config");
|
||||
pw.println(" Reset the configuration set with \"update-config\"");
|
||||
pw.println();
|
||||
pw.println("cmd shortcut clear-default-launcher [--user USER_ID]");
|
||||
pw.println(" Clear the cached default launcher");
|
||||
pw.println();
|
||||
pw.println("cmd shortcut get-default-launcher [--user USER_ID]");
|
||||
pw.println(" Show the cached default launcher");
|
||||
pw.println();
|
||||
pw.println("cmd shortcut refresh-default-launcher [--user USER_ID]");
|
||||
pw.println(" Refresh the cached default launcher");
|
||||
pw.println();
|
||||
}
|
||||
|
||||
private int handleResetThrottling() {
|
||||
@@ -1569,49 +1712,67 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int handleResetPackageThrottling() {
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
private void handleResetPackageThrottling() throws CommandException {
|
||||
parseOptions(/* takeUser =*/ true);
|
||||
|
||||
int userId = UserHandle.USER_SYSTEM;
|
||||
String opt;
|
||||
while ((opt = getNextOption()) != null) {
|
||||
switch (opt) {
|
||||
case "--user":
|
||||
userId = UserHandle.parseUserArg(getNextArgRequired());
|
||||
break;
|
||||
default:
|
||||
pw.println("Error: Unknown option: " + opt);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
final String packageName = getNextArgRequired();
|
||||
|
||||
synchronized (mLock) {
|
||||
getPackageShortcutsLocked(packageName, userId).resetRateLimitingForCommandLine();
|
||||
saveUserLocked(userId);
|
||||
getPackageShortcutsLocked(packageName, mUserId).resetRateLimitingForCommandLine();
|
||||
saveUserLocked(mUserId);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int handleOverrideConfig() {
|
||||
final PrintWriter pw = getOutPrintWriter();
|
||||
private void handleOverrideConfig() throws CommandException {
|
||||
final String config = getNextArgRequired();
|
||||
|
||||
synchronized (mLock) {
|
||||
if (!updateConfigurationLocked(config)) {
|
||||
pw.println("override-config failed. See logcat for details.");
|
||||
return 1;
|
||||
throw new CommandException("override-config failed. See logcat for details.");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private int handleResetConfig() {
|
||||
private void handleResetConfig() {
|
||||
synchronized (mLock) {
|
||||
loadConfigurationLocked();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private void clearLauncher() {
|
||||
synchronized (mLock) {
|
||||
getUserShortcutsLocked(mUserId).setLauncherComponent(
|
||||
ShortcutService.this, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void showLauncher() {
|
||||
synchronized (mLock) {
|
||||
// This ensures to set the cached launcher. Package name doesn't matter.
|
||||
hasShortcutHostPermissionInner("-", mUserId);
|
||||
|
||||
getOutPrintWriter().println("Launcher: "
|
||||
+ getUserShortcutsLocked(mUserId).getLauncherComponent());
|
||||
}
|
||||
}
|
||||
|
||||
private void handleClearDefaultLauncher() throws CommandException {
|
||||
parseOptions(/* takeUser =*/ true);
|
||||
|
||||
clearLauncher();
|
||||
}
|
||||
|
||||
private void handleGetDefaultLauncher() throws CommandException {
|
||||
parseOptions(/* takeUser =*/ true);
|
||||
|
||||
showLauncher();
|
||||
}
|
||||
|
||||
private void handleRefreshDefaultLauncher() throws CommandException {
|
||||
parseOptions(/* takeUser =*/ true);
|
||||
|
||||
clearLauncher();
|
||||
showLauncher();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1640,6 +1801,10 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
return ActivityManager.isLowRamDeviceStatic();
|
||||
}
|
||||
|
||||
PackageManagerInternal injectPackageManagerInternal() {
|
||||
return mPackageManagerInternal;
|
||||
}
|
||||
|
||||
File getUserBitmapFilePath(@UserIdInt int userId) {
|
||||
return new File(injectUserDataPath(userId), DIRECTORY_BITMAPS);
|
||||
}
|
||||
@@ -1687,6 +1852,9 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Per-user information.
|
||||
*/
|
||||
class UserShortcuts {
|
||||
private static final String TAG = ShortcutService.TAG;
|
||||
|
||||
@@ -1695,6 +1863,8 @@ class UserShortcuts {
|
||||
|
||||
private final ArrayMap<String, PackageShortcuts> mPackages = new ArrayMap<>();
|
||||
|
||||
private ComponentName mLauncherComponent;
|
||||
|
||||
public UserShortcuts(int userId) {
|
||||
mUserId = userId;
|
||||
}
|
||||
@@ -1706,11 +1876,11 @@ class UserShortcuts {
|
||||
public void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
|
||||
out.startTag(null, ShortcutService.TAG_USER);
|
||||
|
||||
for (int i = 0; i < mPackages.size(); i++) {
|
||||
final String packageName = mPackages.keyAt(i);
|
||||
final PackageShortcuts packageShortcuts = mPackages.valueAt(i);
|
||||
ShortcutService.writeTagValue(out, ShortcutService.TAG_LAUNCHER,
|
||||
mLauncherComponent);
|
||||
|
||||
packageShortcuts.saveToXml(out);
|
||||
for (int i = 0; i < mPackages.size(); i++) {
|
||||
mPackages.valueAt(i).saveToXml(out);
|
||||
}
|
||||
|
||||
out.endTag(null, ShortcutService.TAG_USER);
|
||||
@@ -1730,6 +1900,10 @@ class UserShortcuts {
|
||||
final int depth = parser.getDepth();
|
||||
final String tag = parser.getName();
|
||||
switch (tag) {
|
||||
case ShortcutService.TAG_LAUNCHER:
|
||||
ret.mLauncherComponent = ShortcutService.parseComponentNameAttribute(
|
||||
parser, ShortcutService.ATTR_VALUE);
|
||||
continue;
|
||||
case ShortcutService.TAG_PACKAGE:
|
||||
final PackageShortcuts shortcuts = PackageShortcuts.loadFromXml(parser, userId);
|
||||
|
||||
@@ -1742,12 +1916,30 @@ class UserShortcuts {
|
||||
return ret;
|
||||
}
|
||||
|
||||
public ComponentName getLauncherComponent() {
|
||||
return mLauncherComponent;
|
||||
}
|
||||
|
||||
public void setLauncherComponent(ShortcutService s, ComponentName launcherComponent) {
|
||||
if (Objects.equal(mLauncherComponent, launcherComponent)) {
|
||||
return;
|
||||
}
|
||||
mLauncherComponent = launcherComponent;
|
||||
s.scheduleSaveUser(mUserId);
|
||||
}
|
||||
|
||||
public void dump(@NonNull ShortcutService s, @NonNull PrintWriter pw, @NonNull String prefix) {
|
||||
pw.print(" ");
|
||||
pw.print(prefix);
|
||||
pw.print("User: ");
|
||||
pw.print(mUserId);
|
||||
pw.println();
|
||||
|
||||
pw.print(prefix);
|
||||
pw.print(" ");
|
||||
pw.print("Default launcher: ");
|
||||
pw.print(mLauncherComponent);
|
||||
pw.println();
|
||||
|
||||
for (int i = 0; i < mPackages.size(); i++) {
|
||||
mPackages.valueAt(i).dump(s, pw, prefix + " ");
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.android.server.pm;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
@@ -27,6 +28,7 @@ import android.content.pm.ILauncherApps;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.LauncherApps.ShortcutQuery;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.pm.ShortcutManager;
|
||||
import android.content.pm.ShortcutServiceInternal;
|
||||
@@ -191,6 +193,17 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
boolean injectIsLowRamDevice() {
|
||||
return mInjectdIsLowRamDevice;
|
||||
}
|
||||
|
||||
@Override
|
||||
PackageManagerInternal injectPackageManagerInternal() {
|
||||
return mMockPackageManagerInternal;
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasShortcutHostPermission(@NonNull String callingPackage, int userId) {
|
||||
// Sort of hack; do a simpler check.
|
||||
return LAUNCHER_1.equals(callingPackage) || LAUNCHER_2.equals(callingPackage);
|
||||
}
|
||||
}
|
||||
|
||||
/** ShortcutManager with injection override methods. */
|
||||
@@ -258,6 +271,7 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
private Map<String, Integer> mInjectedPackageUidMap;
|
||||
|
||||
private PackageManager mMockPackageManager;
|
||||
private PackageManagerInternal mMockPackageManagerInternal;
|
||||
private UserManager mMockUserManager;
|
||||
|
||||
private static final String CALLING_PACKAGE_1 = "com.android.test.1";
|
||||
@@ -298,6 +312,7 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
mClientContext = new ClientContext();
|
||||
|
||||
mMockPackageManager = mock(PackageManager.class);
|
||||
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
|
||||
mMockUserManager = mock(UserManager.class);
|
||||
|
||||
// Prepare injection values.
|
||||
@@ -1889,6 +1904,9 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
assertEquals(2, mManager.getRemainingCallCount());
|
||||
});
|
||||
|
||||
mService.getShortcutsForTest().get(UserHandle.USER_SYSTEM).setLauncherComponent(
|
||||
mService, new ComponentName("pkg1", "class"));
|
||||
|
||||
// Restore.
|
||||
initService();
|
||||
|
||||
@@ -1918,6 +1936,9 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
assertEquals("title2-2", getCallerShortcut("s2").getTitle());
|
||||
});
|
||||
|
||||
assertEquals("pkg1", mService.getShortcutsForTest().get(UserHandle.USER_SYSTEM)
|
||||
.getLauncherComponent().getPackageName());
|
||||
|
||||
// Start another user
|
||||
mService.onStartUserLocked(USER_10);
|
||||
|
||||
@@ -1932,6 +1953,7 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
assertEquals("title10-1-1", getCallerShortcut("s1").getTitle());
|
||||
assertEquals("title10-1-2", getCallerShortcut("s2").getTitle());
|
||||
});
|
||||
assertNull(mService.getShortcutsForTest().get(USER_10).getLauncherComponent());
|
||||
|
||||
// Try stopping the user
|
||||
mService.onCleanupUserInner(USER_10);
|
||||
@@ -1941,4 +1963,8 @@ public class ShortcutManagerTest extends AndroidTestCase {
|
||||
|
||||
// TODO Check all other fields
|
||||
}
|
||||
|
||||
// TODO Detailed test for hasShortcutPermissionInner().
|
||||
|
||||
// TODO Add tests for the command line functions too.
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user