/* * Copyright (C) 2014 The Android Open Source 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 android.content.pm; import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.TestApi; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager.ApplicationInfoFlags; import android.graphics.Rect; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.os.UserManager; import android.util.Log; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; /** * Class for retrieving a list of launchable activities for the current user and any associated * managed profiles. This is mainly for use by launchers. Apps can be queried for each user profile. * Since the PackageManager will not deliver package broadcasts for other profiles, you can register * for package changes here. *
* To watch for managed profiles being added or removed, register for the following broadcasts: * {@link Intent#ACTION_MANAGED_PROFILE_ADDED} and {@link Intent#ACTION_MANAGED_PROFILE_REMOVED}. *
* You can retrieve the list of profiles associated with this user with
* {@link UserManager#getUserProfiles()}.
*/
public class LauncherApps {
static final String TAG = "LauncherApps";
static final boolean DEBUG = false;
private Context mContext;
private ILauncherApps mService;
private PackageManager mPm;
private List 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). Only "key"
* information will be provided, as defined in {@link ShortcutInfo#hasKeyFieldsOnly()}.
* @param user The UserHandle of the profile that generated the change.
*
* @hide
*/
public void onShortcutsChanged(@NonNull String packageName,
@NonNull List Only the default launcher can access the shortcut information.
*
* 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 not have to purge pinned shortcut information.
*
* @hide
*/
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}.
*
* 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.
*
* @hide
*/
@Nullable
public List This API is NOT cumulative; this will replace all pinned shortcuts for the package.
* However, different launchers may have different set of pinned shortcuts.
*
* 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.
*
* @hide
*/
public void pinShortcuts(@NonNull String packageName, @NonNull List Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}.
*
* @param shortcut The target shortcut.
*
* @hide
*/
public ParcelFileDescriptor getShortcutIconFd(
@NonNull ShortcutInfo shortcut) {
return getShortcutIconFd(shortcut.getPackageName(), shortcut.getId(),
shortcut.getUserId());
}
/**
* Return the icon as {@link ParcelFileDescriptor}, when it's stored as a file
* (i.e. when {@link ShortcutInfo#hasIconFile()} returns {@code true}).
*
* Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}.
*
* @param packageName The target package name.
* @param shortcutId The ID of the shortcut to lad rom.
* @param user The UserHandle of the profile.
*
* @hide
*/
public ParcelFileDescriptor getShortcutIconFd(
@NonNull String packageName, @NonNull String shortcutId, @NonNull UserHandle user) {
return getShortcutIconFd(packageName, shortcutId, user.getIdentifier());
}
private ParcelFileDescriptor getShortcutIconFd(
@NonNull String packageName, @NonNull String shortcutId, int userId) {
try {
return mService.getShortcutIconFd(mContext.getPackageName(),
packageName, shortcutId, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Launches a shortcut.
*
* 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.
* @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity.
* @param user The UserHandle of the profile.
* @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.
*
* @hide
*/
public boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
@NonNull UserHandle user) {
return startShortcut(packageName, shortcutId, sourceBounds, startActivityOptions,
user.getIdentifier());
}
/**
* Launches a shortcut.
*
* Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}.
*
* @param shortcut The target shortcut.
* @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity.
* @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.
*
* @hide
*/
public boolean startShortcut(@NonNull ShortcutInfo shortcut,
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions) {
return startShortcut(shortcut.getPackageName(), shortcut.getId(),
sourceBounds, startActivityOptions,
shortcut.getUserId());
}
private boolean startShortcut(@NonNull String packageName, @NonNull String shortcutId,
@Nullable Rect sourceBounds, @Nullable Bundle startActivityOptions,
int userId) {
try {
return mService.startShortcut(mContext.getPackageName(), packageName, shortcutId,
sourceBounds, startActivityOptions, userId);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
/**
* Registers a callback for changes to packages in current and managed profiles.
*
* @param callback The callback to register.
*/
public void registerCallback(Callback callback) {
registerCallback(callback, null);
}
/**
* Registers a callback for changes to packages in current and managed profiles.
*
* @param callback The callback to register.
* @param handler that should be used to post callbacks on, may be null.
*/
public void registerCallback(Callback callback, Handler handler) {
synchronized (this) {
if (callback != null && findCallbackLocked(callback) < 0) {
boolean addedFirstCallback = mCallbacks.size() == 0;
addCallbackLocked(callback, handler);
if (addedFirstCallback) {
try {
mService.addOnAppsChangedListener(mContext.getPackageName(),
mAppsChangedListener);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
}
}
}
/**
* Unregisters a callback that was previously registered.
*
* @param callback The callback to unregister.
* @see #registerCallback(Callback)
*/
public void unregisterCallback(Callback callback) {
synchronized (this) {
removeCallbackLocked(callback);
if (mCallbacks.size() == 0) {
try {
mService.removeOnAppsChangedListener(mAppsChangedListener);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
}
}
}
/** @return position in mCallbacks for callback or -1 if not present. */
private int findCallbackLocked(Callback callback) {
if (callback == null) {
throw new IllegalArgumentException("Callback cannot be null");
}
final int size = mCallbacks.size();
for (int i = 0; i < size; ++i) {
if (mCallbacks.get(i).mCallback == callback) {
return i;
}
}
return -1;
}
private void removeCallbackLocked(Callback callback) {
int pos = findCallbackLocked(callback);
if (pos >= 0) {
mCallbacks.remove(pos);
}
}
private void addCallbackLocked(Callback callback, Handler handler) {
// Remove if already present.
removeCallbackLocked(callback);
if (handler == null) {
handler = new Handler();
}
CallbackMessageHandler toAdd = new CallbackMessageHandler(handler.getLooper(), callback);
mCallbacks.add(toAdd);
}
private IOnAppsChangedListener.Stub mAppsChangedListener = new IOnAppsChangedListener.Stub() {
@Override
public void onPackageRemoved(UserHandle user, String packageName)
throws RemoteException {
if (DEBUG) {
Log.d(TAG, "onPackageRemoved " + user.getIdentifier() + "," + packageName);
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
callback.postOnPackageRemoved(packageName, user);
}
}
}
@Override
public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
if (DEBUG) {
Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
callback.postOnPackageChanged(packageName, user);
}
}
}
@Override
public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
if (DEBUG) {
Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
callback.postOnPackageAdded(packageName, user);
}
}
}
@Override
public void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing)
throws RemoteException {
if (DEBUG) {
Log.d(TAG, "onPackagesAvailable " + user.getIdentifier() + "," + packageNames);
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
callback.postOnPackagesAvailable(packageNames, user, replacing);
}
}
}
@Override
public void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing)
throws RemoteException {
if (DEBUG) {
Log.d(TAG, "onPackagesUnavailable " + user.getIdentifier() + "," + packageNames);
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
callback.postOnPackagesUnavailable(packageNames, user, replacing);
}
}
}
@Override
public void onPackagesSuspended(UserHandle user, String[] packageNames)
throws RemoteException {
if (DEBUG) {
Log.d(TAG, "onPackagesSuspended " + user.getIdentifier() + "," + packageNames);
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
callback.postOnPackagesSuspended(packageNames, user);
}
}
}
@Override
public void onPackagesUnsuspended(UserHandle user, String[] packageNames)
throws RemoteException {
if (DEBUG) {
Log.d(TAG, "onPackagesUnsuspended " + user.getIdentifier() + "," + packageNames);
}
synchronized (LauncherApps.this) {
for (CallbackMessageHandler callback : mCallbacks) {
callback.postOnPackagesUnsuspended(packageNames, user);
}
}
}
@Override
public void onShortcutChanged(UserHandle user, String packageName,
ParceledListSlice shortcuts) {
if (DEBUG) {
Log.d(TAG, "onShortcutChanged " + user.getIdentifier() + "," + packageName);
}
final List