LauncherAppsService.java revision 932249db930d78131c4314ff46ec5fca76cb46cd
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.pm;
18
19import android.app.AppGlobals;
20import android.content.BroadcastReceiver;
21import android.content.ComponentName;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.ActivityInfo;
26import android.content.pm.ILauncherApps;
27import android.content.pm.IOnAppsChangedListener;
28import android.content.pm.IPackageManager;
29import android.content.pm.PackageManager;
30import android.content.pm.PackageInfo;
31import android.content.pm.ResolveInfo;
32import android.content.pm.UserInfo;
33import android.graphics.Rect;
34import android.os.Binder;
35import android.os.Bundle;
36import android.os.IInterface;
37import android.os.RemoteCallbackList;
38import android.os.RemoteException;
39import android.os.UserHandle;
40import android.os.UserManager;
41import android.util.Log;
42import android.util.Slog;
43
44import com.android.internal.content.PackageMonitor;
45
46import java.util.ArrayList;
47import java.util.List;
48
49/**
50 * Service that manages requests and callbacks for launchers that support
51 * managed profiles.
52 */
53public class LauncherAppsService extends ILauncherApps.Stub {
54    private static final boolean DEBUG = false;
55    private static final String TAG = "LauncherAppsService";
56    private final Context mContext;
57    private final PackageManager mPm;
58    private final UserManager mUm;
59    private final PackageCallbackList<IOnAppsChangedListener> mListeners
60            = new PackageCallbackList<IOnAppsChangedListener>();
61
62    private MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
63
64    public LauncherAppsService(Context context) {
65        mContext = context;
66        mPm = mContext.getPackageManager();
67        mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
68    }
69
70    /*
71     * @see android.content.pm.ILauncherApps#addOnAppsChangedListener(
72     *          android.content.pm.IOnAppsChangedListener)
73     */
74    @Override
75    public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException {
76        synchronized (mListeners) {
77            if (DEBUG) {
78                Log.d(TAG, "Adding listener from " + Binder.getCallingUserHandle());
79            }
80            if (mListeners.getRegisteredCallbackCount() == 0) {
81                if (DEBUG) {
82                    Log.d(TAG, "Starting package monitoring");
83                }
84                startWatchingPackageBroadcasts();
85            }
86            mListeners.unregister(listener);
87            mListeners.register(listener, Binder.getCallingUserHandle());
88        }
89    }
90
91    /*
92     * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener(
93     *          android.content.pm.IOnAppsChangedListener)
94     */
95    @Override
96    public void removeOnAppsChangedListener(IOnAppsChangedListener listener)
97            throws RemoteException {
98        synchronized (mListeners) {
99            if (DEBUG) {
100                Log.d(TAG, "Removing listener from " + Binder.getCallingUserHandle());
101            }
102            mListeners.unregister(listener);
103            if (mListeners.getRegisteredCallbackCount() == 0) {
104                stopWatchingPackageBroadcasts();
105            }
106        }
107    }
108
109    /**
110     * Register a receiver to watch for package broadcasts
111     */
112    private void startWatchingPackageBroadcasts() {
113        mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
114    }
115
116    /**
117     * Unregister package broadcast receiver
118     */
119    private void stopWatchingPackageBroadcasts() {
120        if (DEBUG) {
121            Log.d(TAG, "Stopped watching for packages");
122        }
123        mPackageMonitor.unregister();
124    }
125
126    void checkCallbackCount() {
127        synchronized (mListeners) {
128            if (DEBUG) {
129                Log.d(TAG, "Callback count = " + mListeners.getRegisteredCallbackCount());
130            }
131            if (mListeners.getRegisteredCallbackCount() == 0) {
132                stopWatchingPackageBroadcasts();
133            }
134        }
135    }
136
137    /**
138     * Checks if the caller is in the same group as the userToCheck.
139     */
140    private void ensureInUserProfiles(UserHandle userToCheck, String message) {
141        final int callingUserId = UserHandle.getCallingUserId();
142        final int targetUserId = userToCheck.getIdentifier();
143
144        if (targetUserId == callingUserId) return;
145
146        long ident = Binder.clearCallingIdentity();
147        try {
148            UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
149            UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
150            if (targetUserInfo == null
151                    || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
152                    || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) {
153                throw new SecurityException(message);
154            }
155        } finally {
156            Binder.restoreCallingIdentity(ident);
157        }
158    }
159
160    @Override
161    public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
162            throws RemoteException {
163        ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
164
165        final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
166        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
167        mainIntent.setPackage(packageName);
168        long ident = Binder.clearCallingIdentity();
169        try {
170            List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0,
171                    user.getIdentifier());
172            return apps;
173        } finally {
174            Binder.restoreCallingIdentity(ident);
175        }
176    }
177
178    @Override
179    public ResolveInfo resolveActivity(Intent intent, UserHandle user)
180            throws RemoteException {
181        ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
182
183        long ident = Binder.clearCallingIdentity();
184        try {
185            ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier());
186            return app;
187        } finally {
188            Binder.restoreCallingIdentity(ident);
189        }
190    }
191
192    @Override
193    public boolean isPackageEnabled(String packageName, UserHandle user)
194            throws RemoteException {
195        ensureInUserProfiles(user, "Cannot check package for unrelated profile " + user);
196        long ident = Binder.clearCallingIdentity();
197        try {
198            IPackageManager pm = AppGlobals.getPackageManager();
199            PackageInfo info = pm.getPackageInfo(packageName, 0, user.getIdentifier());
200            return info != null && info.applicationInfo.enabled;
201        } finally {
202            Binder.restoreCallingIdentity(ident);
203        }
204    }
205
206    @Override
207    public boolean isActivityEnabled(ComponentName component, UserHandle user)
208            throws RemoteException {
209        ensureInUserProfiles(user, "Cannot check component for unrelated profile " + user);
210        long ident = Binder.clearCallingIdentity();
211        try {
212            IPackageManager pm = AppGlobals.getPackageManager();
213            ActivityInfo info = pm.getActivityInfo(component, 0, user.getIdentifier());
214            return info != null && info.isEnabled();
215        } finally {
216            Binder.restoreCallingIdentity(ident);
217        }
218    }
219
220    @Override
221    public void startActivityAsUser(ComponentName component, Rect sourceBounds,
222            Bundle opts, UserHandle user) throws RemoteException {
223        ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
224
225        Intent launchIntent = new Intent(Intent.ACTION_MAIN);
226        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
227        launchIntent.setComponent(component);
228        launchIntent.setSourceBounds(sourceBounds);
229        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
230
231        final int callingUserId = UserHandle.getCallingUserId();
232        long ident = Binder.clearCallingIdentity();
233        try {
234            mContext.startActivityAsUser(launchIntent, opts, user);
235        } finally {
236            Binder.restoreCallingIdentity(ident);
237        }
238    }
239
240    private class MyPackageMonitor extends PackageMonitor {
241
242        /** Checks if user is a profile of or same as listeningUser. */
243        private boolean isProfileOf(UserHandle user, UserHandle listeningUser, String debugMsg) {
244            if (user.getIdentifier() == listeningUser.getIdentifier()) {
245                if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
246                return true;
247            }
248            long ident = Binder.clearCallingIdentity();
249            try {
250                UserInfo userInfo = mUm.getUserInfo(user.getIdentifier());
251                UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
252                if (userInfo == null || listeningUserInfo == null
253                        || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
254                        || userInfo.profileGroupId != listeningUserInfo.profileGroupId) {
255                    if (DEBUG) {
256                        Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
257                                + debugMsg);
258                    }
259                    return false;
260                } else {
261                    if (DEBUG) {
262                        Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":"
263                                + debugMsg);
264                    }
265                    return true;
266                }
267            } finally {
268                Binder.restoreCallingIdentity(ident);
269            }
270        }
271
272        @Override
273        public void onPackageAdded(String packageName, int uid) {
274            UserHandle user = new UserHandle(getChangingUserId());
275            final int n = mListeners.beginBroadcast();
276            for (int i = 0; i < n; i++) {
277                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
278                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
279                if (!isProfileOf(user, listeningUser, "onPackageAdded")) continue;
280                try {
281                    listener.onPackageAdded(user, packageName);
282                } catch (RemoteException re) {
283                    Slog.d(TAG, "Callback failed ", re);
284                }
285            }
286            mListeners.finishBroadcast();
287
288            super.onPackageAdded(packageName, uid);
289        }
290
291        @Override
292        public void onPackageRemoved(String packageName, int uid) {
293            UserHandle user = new UserHandle(getChangingUserId());
294            final int n = mListeners.beginBroadcast();
295            for (int i = 0; i < n; i++) {
296                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
297                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
298                if (!isProfileOf(user, listeningUser, "onPackageRemoved")) continue;
299                try {
300                    listener.onPackageRemoved(user, packageName);
301                } catch (RemoteException re) {
302                    Slog.d(TAG, "Callback failed ", re);
303                }
304            }
305            mListeners.finishBroadcast();
306
307            super.onPackageRemoved(packageName, uid);
308        }
309
310        @Override
311        public void onPackageModified(String packageName) {
312            UserHandle user = new UserHandle(getChangingUserId());
313            final int n = mListeners.beginBroadcast();
314            for (int i = 0; i < n; i++) {
315                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
316                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
317                if (!isProfileOf(user, listeningUser, "onPackageModified")) continue;
318                try {
319                    listener.onPackageChanged(user, packageName);
320                } catch (RemoteException re) {
321                    Slog.d(TAG, "Callback failed ", re);
322                }
323            }
324            mListeners.finishBroadcast();
325
326            super.onPackageModified(packageName);
327        }
328
329        @Override
330        public void onPackagesAvailable(String[] packages) {
331            UserHandle user = new UserHandle(getChangingUserId());
332            final int n = mListeners.beginBroadcast();
333            for (int i = 0; i < n; i++) {
334                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
335                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
336                if (!isProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
337                try {
338                    listener.onPackagesAvailable(user, packages, isReplacing());
339                } catch (RemoteException re) {
340                    Slog.d(TAG, "Callback failed ", re);
341                }
342            }
343            mListeners.finishBroadcast();
344
345            super.onPackagesAvailable(packages);
346        }
347
348        @Override
349        public void onPackagesUnavailable(String[] packages) {
350            UserHandle user = new UserHandle(getChangingUserId());
351            final int n = mListeners.beginBroadcast();
352            for (int i = 0; i < n; i++) {
353                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
354                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
355                if (!isProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
356                try {
357                    listener.onPackagesUnavailable(user, packages, isReplacing());
358                } catch (RemoteException re) {
359                    Slog.d(TAG, "Callback failed ", re);
360                }
361            }
362            mListeners.finishBroadcast();
363
364            super.onPackagesUnavailable(packages);
365        }
366
367    }
368
369    class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
370        @Override
371        public void onCallbackDied(T callback, Object cookie) {
372            checkCallbackCount();
373        }
374    }
375}
376