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