Launcher APIs and broadcasts for managed profiles
UserManager - Corp badging - Querying list of managed profiles Launcher API - LauncherApps and Service to proxy changes in managed profile to the launcher in the primary profile - Querying and launching launchable apps across profiles Change-Id: Id8f7b4201afdfb5f414d04156d7b81300119289e
This commit is contained in:
@ -119,6 +119,8 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/content/ISyncContext.aidl \
|
||||
core/java/android/content/ISyncServiceAdapter.aidl \
|
||||
core/java/android/content/ISyncStatusObserver.aidl \
|
||||
core/java/android/content/pm/ILauncherApps.aidl \
|
||||
core/java/android/content/pm/IOnAppsChangedListener.aidl \
|
||||
core/java/android/content/pm/IPackageDataObserver.aidl \
|
||||
core/java/android/content/pm/IPackageDeleteObserver.aidl \
|
||||
core/java/android/content/pm/IPackageInstallObserver.aidl \
|
||||
|
@ -6476,6 +6476,7 @@ package android.content {
|
||||
field public static final java.lang.String INPUT_METHOD_SERVICE = "input_method";
|
||||
field public static final java.lang.String INPUT_SERVICE = "input";
|
||||
field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
|
||||
field public static final java.lang.String LAUNCHER_APPS_SERVICE = "launcherapps";
|
||||
field public static final java.lang.String LAYOUT_INFLATER_SERVICE = "layout_inflater";
|
||||
field public static final java.lang.String LOCATION_SERVICE = "location";
|
||||
field public static final java.lang.String MEDIA_ROUTER_SERVICE = "media_router";
|
||||
@ -7650,6 +7651,32 @@ package android.content.pm {
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
}
|
||||
|
||||
public class LauncherActivityInfo {
|
||||
method public int getApplicationFlags();
|
||||
method public android.graphics.drawable.Drawable getBadgedIcon(int);
|
||||
method public android.content.ComponentName getComponentName();
|
||||
method public long getFirstInstallTime();
|
||||
method public android.graphics.drawable.Drawable getIcon(int);
|
||||
method public java.lang.CharSequence getLabel();
|
||||
method public android.os.UserHandle getUser();
|
||||
}
|
||||
|
||||
public class LauncherApps {
|
||||
method public synchronized void addOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
|
||||
method public java.util.List<android.content.pm.LauncherActivityInfo> getActivityList(java.lang.String, android.os.UserHandle);
|
||||
method public synchronized void removeOnAppsChangedListener(android.content.pm.LauncherApps.OnAppsChangedListener);
|
||||
method public android.content.pm.LauncherActivityInfo resolveActivity(android.content.Intent, android.os.UserHandle);
|
||||
method public void startActivityForProfile(android.content.ComponentName, android.graphics.Rect, android.os.Bundle, android.os.UserHandle);
|
||||
}
|
||||
|
||||
public static abstract interface LauncherApps.OnAppsChangedListener {
|
||||
method public abstract void onPackageAdded(android.os.UserHandle, java.lang.String);
|
||||
method public abstract void onPackageChanged(android.os.UserHandle, java.lang.String);
|
||||
method public abstract void onPackageRemoved(android.os.UserHandle, java.lang.String);
|
||||
method public abstract void onPackagesAvailable(android.os.UserHandle, java.lang.String[], boolean);
|
||||
method public abstract void onPackagesUnavailable(android.os.UserHandle, java.lang.String[], boolean);
|
||||
}
|
||||
|
||||
public class PackageInfo implements android.os.Parcelable {
|
||||
ctor public PackageInfo();
|
||||
method public int describeContents();
|
||||
@ -19898,6 +19925,7 @@ package android.os {
|
||||
method public int getUserCount();
|
||||
method public android.os.UserHandle getUserForSerialNumber(long);
|
||||
method public java.lang.String getUserName();
|
||||
method public java.util.List<android.os.UserHandle> getUserProfiles();
|
||||
method public android.os.Bundle getUserRestrictions();
|
||||
method public android.os.Bundle getUserRestrictions(android.os.UserHandle);
|
||||
method public boolean isUserAGoat();
|
||||
|
@ -36,7 +36,9 @@ import android.content.ReceiverCallNotAllowedException;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.ILauncherApps;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.res.AssetManager;
|
||||
@ -605,6 +607,14 @@ class ContextImpl extends Context {
|
||||
}
|
||||
});
|
||||
|
||||
registerService(LAUNCHER_APPS_SERVICE, new ServiceFetcher() {
|
||||
public Object createService(ContextImpl ctx) {
|
||||
IBinder b = ServiceManager.getService(LAUNCHER_APPS_SERVICE);
|
||||
ILauncherApps service = ILauncherApps.Stub.asInterface(b);
|
||||
return new LauncherApps(ctx, service);
|
||||
}
|
||||
});
|
||||
|
||||
registerService(PRINT_SERVICE, new ServiceFetcher() {
|
||||
public Object createService(ContextImpl ctx) {
|
||||
IBinder iBinder = ServiceManager.getService(Context.PRINT_SERVICE);
|
||||
|
@ -1999,6 +1999,7 @@ public abstract class Context {
|
||||
BLUETOOTH_SERVICE,
|
||||
//@hide: SIP_SERVICE,
|
||||
USB_SERVICE,
|
||||
LAUNCHER_APPS_SERVICE,
|
||||
//@hide: SERIAL_SERVICE,
|
||||
INPUT_SERVICE,
|
||||
DISPLAY_SERVICE,
|
||||
@ -2561,6 +2562,16 @@ public abstract class Context {
|
||||
*/
|
||||
public static final String USER_SERVICE = "user";
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService} to retrieve a
|
||||
* {@link android.content.pm.LauncherApps} for querying and monitoring launchable apps across
|
||||
* profiles of a user.
|
||||
*
|
||||
* @see #getSystemService
|
||||
* @see android.content.pm.LauncherApps
|
||||
*/
|
||||
public static final String LAUNCHER_APPS_SERVICE = "launcherapps";
|
||||
|
||||
/**
|
||||
* Use with {@link #getSystemService} to retrieve a
|
||||
* {@link android.app.AppOpsManager} for tracking application operations
|
||||
|
38
core/java/android/content/pm/ILauncherApps.aidl
Normal file
38
core/java/android/content/pm/ILauncherApps.aidl
Normal file
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.content.ComponentName;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.IOnAppsChangedListener;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
interface ILauncherApps {
|
||||
void addOnAppsChangedListener(in IOnAppsChangedListener listener);
|
||||
void removeOnAppsChangedListener(in IOnAppsChangedListener listener);
|
||||
List<ResolveInfo> getLauncherActivities(String packageName, in UserHandle user);
|
||||
ResolveInfo resolveActivity(in Intent intent, in UserHandle user);
|
||||
void startActivityAsUser(in ComponentName component, in Rect sourceBounds,
|
||||
in Bundle opts, in UserHandle user);
|
||||
}
|
30
core/java/android/content/pm/IOnAppsChangedListener.aidl
Normal file
30
core/java/android/content/pm/IOnAppsChangedListener.aidl
Normal file
@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 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.os.UserHandle;
|
||||
|
||||
/**
|
||||
* {@hide}
|
||||
*/
|
||||
oneway interface IOnAppsChangedListener {
|
||||
void onPackageRemoved(in UserHandle user, String packageName);
|
||||
void onPackageAdded(in UserHandle user, String packageName);
|
||||
void onPackageChanged(in UserHandle user, String packageName);
|
||||
void onPackagesAvailable(in UserHandle user, in String[] packageNames, boolean replacing);
|
||||
void onPackagesUnavailable(in UserHandle user, in String[] packageNames, boolean replacing);
|
||||
}
|
146
core/java/android/content/pm/LauncherActivityInfo.java
Normal file
146
core/java/android/content/pm/LauncherActivityInfo.java
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
|
||||
/**
|
||||
* A representation of an activity that can belong to this user or a managed
|
||||
* profile associated with this user. It can be used to query the label, icon
|
||||
* and badged icon for the activity.
|
||||
*/
|
||||
public class LauncherActivityInfo {
|
||||
private static final boolean DEBUG = false;
|
||||
private final PackageManager mPm;
|
||||
private final UserManager mUm;
|
||||
|
||||
private ActivityInfo mActivityInfo;
|
||||
private ComponentName mComponentName;
|
||||
private UserHandle mUser;
|
||||
// TODO: Fetch this value from PM
|
||||
private long mFirstInstallTime;
|
||||
|
||||
/**
|
||||
* Create a launchable activity object for a given ResolveInfo and user.
|
||||
*
|
||||
* @param context The context for fetching resources.
|
||||
* @param info ResolveInfo from which to create the LauncherActivityInfo.
|
||||
* @param user The UserHandle of the profile to which this activity belongs.
|
||||
*/
|
||||
LauncherActivityInfo(Context context, ResolveInfo info, UserHandle user) {
|
||||
this(context);
|
||||
this.mActivityInfo = info.activityInfo;
|
||||
this.mComponentName = LauncherApps.getComponentName(info);
|
||||
this.mUser = user;
|
||||
}
|
||||
|
||||
LauncherActivityInfo(Context context) {
|
||||
mPm = context.getPackageManager();
|
||||
mUm = UserManager.get(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the component name of this activity.
|
||||
*
|
||||
* @return ComponentName of the activity
|
||||
*/
|
||||
public ComponentName getComponentName() {
|
||||
return mComponentName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user handle of the user profile that this activity belongs to.
|
||||
*
|
||||
* @return The UserHandle of the profile.
|
||||
*/
|
||||
public UserHandle getUser() {
|
||||
return mUser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the label for the activity.
|
||||
*
|
||||
* @return The label for the activity.
|
||||
*/
|
||||
public CharSequence getLabel() {
|
||||
return mActivityInfo.loadLabel(mPm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the icon for this activity, without any badging for the profile.
|
||||
* @param density The preferred density of the icon, zero for default density.
|
||||
* @see #getBadgedIcon(int)
|
||||
* @return The drawable associated with the activity
|
||||
*/
|
||||
public Drawable getIcon(int density) {
|
||||
// TODO: Use density
|
||||
return mActivityInfo.loadIcon(mPm);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the application flags from the ApplicationInfo of the activity.
|
||||
*
|
||||
* @return Application flags
|
||||
*/
|
||||
public int getApplicationFlags() {
|
||||
return mActivityInfo.applicationInfo.flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the time at which the package was first installed.
|
||||
* @return The time of installation of the package, in milliseconds.
|
||||
*/
|
||||
public long getFirstInstallTime() {
|
||||
return mFirstInstallTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the activity icon with badging appropriate for the profile.
|
||||
* @param density Optional density for the icon, or 0 to use the default density.
|
||||
* @return A badged icon for the activity.
|
||||
*/
|
||||
public Drawable getBadgedIcon(int density) {
|
||||
// TODO: Handle density
|
||||
if (mUser.equals(android.os.Process.myUserHandle())) {
|
||||
return mActivityInfo.loadIcon(mPm);
|
||||
}
|
||||
Drawable originalIcon = mActivityInfo.loadIcon(mPm);
|
||||
if (originalIcon == null) {
|
||||
if (DEBUG) {
|
||||
Log.w("LauncherActivityInfo", "Couldn't find icon for activity");
|
||||
}
|
||||
originalIcon = mPm.getDefaultActivityIcon();
|
||||
}
|
||||
if (originalIcon instanceof BitmapDrawable) {
|
||||
return mUm.getBadgedDrawableForUser(
|
||||
originalIcon, mUser);
|
||||
}
|
||||
return originalIcon;
|
||||
}
|
||||
}
|
283
core/java/android/content/pm/LauncherApps.java
Normal file
283
core/java/android/content/pm/LauncherApps.java
Normal file
@ -0,0 +1,283 @@
|
||||
/*
|
||||
* 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.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ILauncherApps;
|
||||
import android.content.pm.IOnAppsChangedListener;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.
|
||||
*/
|
||||
public class LauncherApps {
|
||||
|
||||
static final String TAG = "LauncherApps";
|
||||
static final boolean DEBUG = false;
|
||||
|
||||
private Context mContext;
|
||||
private ILauncherApps mService;
|
||||
|
||||
private List<OnAppsChangedListener> mListeners
|
||||
= new ArrayList<OnAppsChangedListener>();
|
||||
|
||||
/**
|
||||
* Callbacks for changes to this and related managed profiles.
|
||||
*/
|
||||
public interface OnAppsChangedListener {
|
||||
/**
|
||||
* Indicates that a package was removed from the specified profile.
|
||||
*
|
||||
* @param user The UserHandle of the profile that generated the change.
|
||||
* @param packageName The name of the package that was removed.
|
||||
*/
|
||||
void onPackageRemoved(UserHandle user, String packageName);
|
||||
|
||||
/**
|
||||
* Indicates that a package was added to the specified profile.
|
||||
*
|
||||
* @param user The UserHandle of the profile that generated the change.
|
||||
* @param packageName The name of the package that was added.
|
||||
*/
|
||||
void onPackageAdded(UserHandle user, String packageName);
|
||||
|
||||
/**
|
||||
* Indicates that a package was modified in the specified profile.
|
||||
*
|
||||
* @param user The UserHandle of the profile that generated the change.
|
||||
* @param packageName The name of the package that has changed.
|
||||
*/
|
||||
void onPackageChanged(UserHandle user, String packageName);
|
||||
|
||||
/**
|
||||
* Indicates that one or more packages have become available. For
|
||||
* example, this can happen when a removable storage card has
|
||||
* reappeared.
|
||||
*
|
||||
* @param user The UserHandle of the profile that generated the change.
|
||||
* @param packageNames The names of the packages that have become
|
||||
* available.
|
||||
* @param replacing Indicates whether these packages are replacing
|
||||
* existing ones.
|
||||
*/
|
||||
void onPackagesAvailable(UserHandle user, String[] packageNames, boolean replacing);
|
||||
|
||||
/**
|
||||
* Indicates that one or more packages have become unavailable. For
|
||||
* example, this can happen when a removable storage card has been
|
||||
* removed.
|
||||
*
|
||||
* @param user The UserHandle of the profile that generated the change.
|
||||
* @param packageNames The names of the packages that have become
|
||||
* unavailable.
|
||||
* @param replacing Indicates whether the packages are about to be
|
||||
* replaced with new versions.
|
||||
*/
|
||||
void onPackagesUnavailable(UserHandle user, String[] packageNames, boolean replacing);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public LauncherApps(Context context, ILauncherApps service) {
|
||||
mContext = context;
|
||||
mService = service;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of launchable activities that match {@link Intent#ACTION_MAIN} and
|
||||
* {@link Intent#CATEGORY_LAUNCHER}, for a specified user.
|
||||
*
|
||||
* @param packageName The specific package to query. If null, it checks all installed packages
|
||||
* in the profile.
|
||||
* @param user The UserHandle of the profile.
|
||||
* @return List of launchable activities. Can be an empty list but will not be null.
|
||||
*/
|
||||
public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) {
|
||||
List<ResolveInfo> activities = null;
|
||||
try {
|
||||
activities = mService.getLauncherActivities(packageName, user);
|
||||
} catch (RemoteException re) {
|
||||
}
|
||||
if (activities == null) {
|
||||
return Collections.EMPTY_LIST;
|
||||
}
|
||||
ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>();
|
||||
final int count = activities.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
ResolveInfo ri = activities.get(i);
|
||||
LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user);
|
||||
if (DEBUG) {
|
||||
Log.v(TAG, "Returning activity for profile " + user + " : "
|
||||
+ lai.getComponentName());
|
||||
}
|
||||
lais.add(lai);
|
||||
}
|
||||
return lais;
|
||||
}
|
||||
|
||||
static ComponentName getComponentName(ResolveInfo ri) {
|
||||
return new ComponentName(ri.activityInfo.packageName, ri.activityInfo.name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the activity info for a given intent and user handle, if it resolves. Otherwise it
|
||||
* returns null.
|
||||
*
|
||||
* @param intent The intent to find a match for.
|
||||
* @param user The profile to look in for a match.
|
||||
* @return An activity info object if there is a match.
|
||||
*/
|
||||
public LauncherActivityInfo resolveActivity(Intent intent, UserHandle user) {
|
||||
try {
|
||||
ResolveInfo ri = mService.resolveActivity(intent, user);
|
||||
if (ri != null) {
|
||||
LauncherActivityInfo info = new LauncherActivityInfo(mContext, ri, user);
|
||||
return info;
|
||||
}
|
||||
} catch (RemoteException re) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts an activity in the specified profile.
|
||||
*
|
||||
* @param component The ComponentName of the activity to launch
|
||||
* @param sourceBounds The Rect containing the source bounds of the clicked icon
|
||||
* @param opts Options to pass to startActivity
|
||||
* @param user The UserHandle of the profile
|
||||
*/
|
||||
public void startActivityForProfile(ComponentName component, Rect sourceBounds,
|
||||
Bundle opts, UserHandle user) {
|
||||
try {
|
||||
mService.startActivityAsUser(component, sourceBounds, opts, user);
|
||||
} catch (RemoteException re) {
|
||||
// Oops!
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a listener for changes to packages in current and managed profiles.
|
||||
*
|
||||
* @param listener The listener to add.
|
||||
*/
|
||||
public synchronized void addOnAppsChangedListener(OnAppsChangedListener listener) {
|
||||
if (listener != null && !mListeners.contains(listener)) {
|
||||
mListeners.add(listener);
|
||||
if (mListeners.size() == 1) {
|
||||
try {
|
||||
mService.addOnAppsChangedListener(mAppsChangedListener);
|
||||
} catch (RemoteException re) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a listener that was previously added.
|
||||
*
|
||||
* @param listener The listener to remove.
|
||||
* @see #addOnAppsChangedListener(OnAppsChangedListener)
|
||||
*/
|
||||
public synchronized void removeOnAppsChangedListener(OnAppsChangedListener listener) {
|
||||
mListeners.remove(listener);
|
||||
if (mListeners.size() == 0) {
|
||||
try {
|
||||
mService.removeOnAppsChangedListener(mAppsChangedListener);
|
||||
} catch (RemoteException re) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 (OnAppsChangedListener listener : mListeners) {
|
||||
listener.onPackageRemoved(user, packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageChanged(UserHandle user, String packageName) throws RemoteException {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onPackageChanged " + user.getIdentifier() + "," + packageName);
|
||||
}
|
||||
synchronized (LauncherApps.this) {
|
||||
for (OnAppsChangedListener listener : mListeners) {
|
||||
listener.onPackageChanged(user, packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageAdded(UserHandle user, String packageName) throws RemoteException {
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onPackageAdded " + user.getIdentifier() + "," + packageName);
|
||||
}
|
||||
synchronized (LauncherApps.this) {
|
||||
for (OnAppsChangedListener listener : mListeners) {
|
||||
listener.onPackageAdded(user, packageName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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 (OnAppsChangedListener listener : mListeners) {
|
||||
listener.onPackagesAvailable(user, packageNames, 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 (OnAppsChangedListener listener : mListeners) {
|
||||
listener.onPackagesUnavailable(user, packageNames, replacing);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@ -20,10 +20,16 @@ import android.content.Context;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Bitmap.Config;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -474,7 +480,7 @@ public class UserManager {
|
||||
/**
|
||||
* Returns list of the profiles of userHandle including
|
||||
* userHandle itself.
|
||||
*
|
||||
*
|
||||
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
|
||||
* @param userHandle profiles of this user will be returned.
|
||||
* @return the list of profiles.
|
||||
@ -490,9 +496,67 @@ public class UserManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information for all users on this device.
|
||||
* Requires {@link android.Manifest.permission#MANAGE_USERS} permission.
|
||||
* @param excludeDying specify if the list should exclude users being removed.
|
||||
* Returns a list of UserHandles for profiles associated with this user, including this user.
|
||||
*
|
||||
* @return A non-empty list of UserHandles associated with the calling user.
|
||||
*/
|
||||
public List<UserHandle> getUserProfiles() {
|
||||
ArrayList<UserHandle> profiles = new ArrayList<UserHandle>();
|
||||
List<UserInfo> users = getProfiles(UserHandle.myUserId());
|
||||
for (UserInfo info : users) {
|
||||
UserHandle userHandle = new UserHandle(info.id);
|
||||
profiles.add(userHandle);
|
||||
}
|
||||
return profiles;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public Drawable getBadgedDrawableForUser(Drawable icon, UserHandle user) {
|
||||
int badgeResId = getBadgeResIdForUser(user.getIdentifier());
|
||||
if (badgeResId == 0) {
|
||||
return icon;
|
||||
} else {
|
||||
Drawable badgeIcon = mContext.getPackageManager()
|
||||
.getDrawable("system", badgeResId, null);
|
||||
return getMergedDrawable(icon, badgeIcon);
|
||||
}
|
||||
}
|
||||
|
||||
private int getBadgeResIdForUser(int userHandle) {
|
||||
// Return the framework-provided badge.
|
||||
if (userHandle == UserHandle.myUserId()) {
|
||||
UserInfo user = getUserInfo(userHandle);
|
||||
/* TODO: Allow managed profiles for other users in the future */
|
||||
if (!user.isManagedProfile()
|
||||
|| user.profileGroupId != getUserInfo(UserHandle.USER_OWNER).profileGroupId) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return com.android.internal.R.drawable.ic_corp_badge;
|
||||
}
|
||||
|
||||
private Drawable getMergedDrawable(Drawable icon, Drawable badge) {
|
||||
final int width = icon.getIntrinsicWidth();
|
||||
final int height = icon.getIntrinsicHeight();
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
icon.setBounds(0, 0, width, height);
|
||||
icon.draw(canvas);
|
||||
badge.setBounds(0, 0, width, height);
|
||||
badge.draw(canvas);
|
||||
BitmapDrawable merged = new BitmapDrawable(bitmap);
|
||||
if (icon instanceof BitmapDrawable) {
|
||||
merged.setTargetDensity(((BitmapDrawable) icon).getBitmap().getDensity());
|
||||
}
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns information for all users on this device. Requires
|
||||
* {@link android.Manifest.permission#MANAGE_USERS} permission.
|
||||
*
|
||||
* @param excludeDying specify if the list should exclude users being
|
||||
* removed.
|
||||
* @return the list of users that were created.
|
||||
* @hide
|
||||
*/
|
||||
|
@ -242,7 +242,11 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver {
|
||||
public boolean anyPackagesDisappearing() {
|
||||
return mDisappearingPackages != null;
|
||||
}
|
||||
|
||||
|
||||
public boolean isReplacing() {
|
||||
return mChangeType == PACKAGE_UPDATING;
|
||||
}
|
||||
|
||||
public boolean isPackageModified(String packageName) {
|
||||
if (mModifiedPackages != null) {
|
||||
for (int i=mModifiedPackages.length-1; i>=0; i--) {
|
||||
|
BIN
core/res/res/drawable-hdpi/ic_corp_badge.png
Normal file
BIN
core/res/res/drawable-hdpi/ic_corp_badge.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
BIN
core/res/res/drawable-xhdpi/ic_corp_badge.png
Normal file
BIN
core/res/res/drawable-xhdpi/ic_corp_badge.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
BIN
core/res/res/drawable-xxhdpi/ic_corp_badge.png
Normal file
BIN
core/res/res/drawable-xxhdpi/ic_corp_badge.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.2 KiB |
@ -1104,6 +1104,7 @@
|
||||
<java-symbol type="drawable" name="cling_button" />
|
||||
<java-symbol type="drawable" name="cling_arrow_up" />
|
||||
<java-symbol type="drawable" name="cling_bg" />
|
||||
<java-symbol type="drawable" name="ic_corp_badge" />
|
||||
|
||||
<java-symbol type="layout" name="action_bar_home" />
|
||||
<java-symbol type="layout" name="action_bar_title_item" />
|
||||
|
@ -0,0 +1,292 @@
|
||||
/*
|
||||
* 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 com.android.server.pm;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.ILauncherApps;
|
||||
import android.content.pm.IOnAppsChangedListener;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.IInterface;
|
||||
import android.os.RemoteCallbackList;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.content.PackageMonitor;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Service that manages requests and callbacks for launchers that support
|
||||
* managed profiles.
|
||||
*/
|
||||
public class LauncherAppsService extends ILauncherApps.Stub {
|
||||
|
||||
private static final String TAG = "LauncherAppsService";
|
||||
private final Context mContext;
|
||||
private final PackageManager mPm;
|
||||
private final UserManager mUm;
|
||||
private final PackageCallbackList<IOnAppsChangedListener> mListeners
|
||||
= new PackageCallbackList<IOnAppsChangedListener>();
|
||||
|
||||
private MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
|
||||
|
||||
public LauncherAppsService(Context context) {
|
||||
mContext = context;
|
||||
mPm = mContext.getPackageManager();
|
||||
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
|
||||
}
|
||||
|
||||
/*
|
||||
* @see android.content.pm.ILauncherApps#addOnAppsChangedListener(
|
||||
* android.content.pm.IOnAppsChangedListener)
|
||||
*/
|
||||
@Override
|
||||
public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException {
|
||||
synchronized (mListeners) {
|
||||
if (mListeners.getRegisteredCallbackCount() == 0) {
|
||||
startWatchingPackageBroadcasts();
|
||||
}
|
||||
mListeners.unregister(listener);
|
||||
mListeners.register(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* @see android.content.pm.ILauncherApps#removeOnAppsChangedListener(
|
||||
* android.content.pm.IOnAppsChangedListener)
|
||||
*/
|
||||
@Override
|
||||
public void removeOnAppsChangedListener(IOnAppsChangedListener listener)
|
||||
throws RemoteException {
|
||||
synchronized (mListeners) {
|
||||
mListeners.unregister(listener);
|
||||
if (mListeners.getRegisteredCallbackCount() == 0) {
|
||||
stopWatchingPackageBroadcasts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a receiver to watch for package broadcasts
|
||||
*/
|
||||
private void startWatchingPackageBroadcasts() {
|
||||
mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister package broadcast receiver
|
||||
*/
|
||||
private void stopWatchingPackageBroadcasts() {
|
||||
mPackageMonitor.unregister();
|
||||
}
|
||||
|
||||
void checkCallbackCount() {
|
||||
synchronized (LauncherAppsService.this) {
|
||||
if (mListeners.getRegisteredCallbackCount() == 0) {
|
||||
stopWatchingPackageBroadcasts();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the caller is in the same group as the userToCheck.
|
||||
*/
|
||||
private void ensureInUserProfiles(UserHandle userToCheck, String message) {
|
||||
final int callingUserId = UserHandle.getCallingUserId();
|
||||
final int targetUserId = userToCheck.getIdentifier();
|
||||
|
||||
if (targetUserId == callingUserId) return;
|
||||
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
|
||||
UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
|
||||
if (targetUserInfo == null
|
||||
|| targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
|
||||
|| targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) {
|
||||
throw new SecurityException(message);
|
||||
}
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
|
||||
throws RemoteException {
|
||||
ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
|
||||
|
||||
final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
|
||||
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0,
|
||||
user.getIdentifier());
|
||||
return apps;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResolveInfo resolveActivity(Intent intent, UserHandle user)
|
||||
throws RemoteException {
|
||||
ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
|
||||
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier());
|
||||
return app;
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startActivityAsUser(ComponentName component, Rect sourceBounds,
|
||||
Bundle opts, UserHandle user) throws RemoteException {
|
||||
ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
|
||||
|
||||
Intent launchIntent = new Intent(Intent.ACTION_MAIN);
|
||||
launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
|
||||
launchIntent.setComponent(component);
|
||||
launchIntent.setSourceBounds(sourceBounds);
|
||||
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
|
||||
final int callingUserId = UserHandle.getCallingUserId();
|
||||
long ident = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mContext.startActivityAsUser(launchIntent, opts, user);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(ident);
|
||||
}
|
||||
}
|
||||
|
||||
private class MyPackageMonitor extends PackageMonitor {
|
||||
|
||||
@Override
|
||||
public void onPackageAdded(String packageName, int uid) {
|
||||
UserHandle user = new UserHandle(getChangingUserId());
|
||||
// TODO: if (!isProfile(user)) return;
|
||||
final int n = mListeners.beginBroadcast();
|
||||
for (int i = 0; i < n; i++) {
|
||||
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
|
||||
try {
|
||||
listener.onPackageAdded(user, packageName);
|
||||
} catch (RemoteException re) {
|
||||
Slog.d(TAG, "Callback failed ", re);
|
||||
}
|
||||
}
|
||||
mListeners.finishBroadcast();
|
||||
|
||||
super.onPackageAdded(packageName, uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageRemoved(String packageName, int uid) {
|
||||
UserHandle user = new UserHandle(getChangingUserId());
|
||||
// TODO: if (!isCurrentProfile(user)) return;
|
||||
final int n = mListeners.beginBroadcast();
|
||||
for (int i = 0; i < n; i++) {
|
||||
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
|
||||
try {
|
||||
listener.onPackageRemoved(user, packageName);
|
||||
} catch (RemoteException re) {
|
||||
Slog.d(TAG, "Callback failed ", re);
|
||||
}
|
||||
}
|
||||
mListeners.finishBroadcast();
|
||||
|
||||
super.onPackageRemoved(packageName, uid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackageModified(String packageName) {
|
||||
UserHandle user = new UserHandle(getChangingUserId());
|
||||
// TODO: if (!isProfile(user)) return;
|
||||
final int n = mListeners.beginBroadcast();
|
||||
for (int i = 0; i < n; i++) {
|
||||
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
|
||||
try {
|
||||
listener.onPackageChanged(user, packageName);
|
||||
} catch (RemoteException re) {
|
||||
Slog.d(TAG, "Callback failed ", re);
|
||||
}
|
||||
}
|
||||
mListeners.finishBroadcast();
|
||||
|
||||
super.onPackageModified(packageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackagesAvailable(String[] packages) {
|
||||
UserHandle user = new UserHandle(getChangingUserId());
|
||||
// TODO: if (!isProfile(user)) return;
|
||||
final int n = mListeners.beginBroadcast();
|
||||
for (int i = 0; i < n; i++) {
|
||||
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
|
||||
try {
|
||||
listener.onPackagesAvailable(user, packages, isReplacing());
|
||||
} catch (RemoteException re) {
|
||||
Slog.d(TAG, "Callback failed ", re);
|
||||
}
|
||||
}
|
||||
mListeners.finishBroadcast();
|
||||
|
||||
super.onPackagesAvailable(packages);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPackagesUnavailable(String[] packages) {
|
||||
UserHandle user = new UserHandle(getChangingUserId());
|
||||
// TODO: if (!isProfile(user)) return;
|
||||
final int n = mListeners.beginBroadcast();
|
||||
for (int i = 0; i < n; i++) {
|
||||
IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
|
||||
try {
|
||||
listener.onPackagesUnavailable(user, packages, isReplacing());
|
||||
} catch (RemoteException re) {
|
||||
Slog.d(TAG, "Callback failed ", re);
|
||||
}
|
||||
}
|
||||
mListeners.finishBroadcast();
|
||||
|
||||
super.onPackagesUnavailable(packages);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
|
||||
|
||||
@Override
|
||||
public void onCallbackDied(T callback, Object cookie) {
|
||||
checkCallbackCount();
|
||||
}
|
||||
}
|
||||
}
|
@ -258,7 +258,9 @@ public class UserManagerService extends IUserManager.Stub {
|
||||
|
||||
@Override
|
||||
public List<UserInfo> getProfiles(int userId) {
|
||||
checkManageUsersPermission("query users");
|
||||
if (userId != UserHandle.getCallingUserId()) {
|
||||
checkManageUsersPermission("getting profiles related to user " + userId);
|
||||
}
|
||||
synchronized (mPackagesLock) {
|
||||
UserInfo user = getUserInfoLocked(userId);
|
||||
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
|
||||
|
@ -70,6 +70,7 @@ import com.android.server.net.NetworkStatsService;
|
||||
import com.android.server.notification.NotificationManagerService;
|
||||
import com.android.server.os.SchedulingPolicyService;
|
||||
import com.android.server.pm.Installer;
|
||||
import com.android.server.pm.LauncherAppsService;
|
||||
import com.android.server.pm.PackageManagerService;
|
||||
import com.android.server.pm.UserManagerService;
|
||||
import com.android.server.power.PowerManagerService;
|
||||
@ -922,6 +923,14 @@ public final class SystemServer {
|
||||
Slog.e(TAG, "Failure starting TrustManagerService", e);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Slog.i(TAG, "LauncherAppsService");
|
||||
LauncherAppsService las = new LauncherAppsService(context);
|
||||
ServiceManager.addService(Context.LAUNCHER_APPS_SERVICE, las);
|
||||
} catch (Throwable t) {
|
||||
reportWtf("starting LauncherAppsService", t);
|
||||
}
|
||||
}
|
||||
|
||||
// Before things start rolling, be sure we have decided whether
|
||||
|
Reference in New Issue
Block a user