LauncherAppsService.java revision 2871654830e94445842d6a67d775d7c317a7f2c0
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          * and the user is enabled. */
244        private boolean isEnabledProfileOf(UserHandle user, UserHandle listeningUser,
245                String debugMsg) {
246            if (user.getIdentifier() == listeningUser.getIdentifier()) {
247                if (DEBUG) Log.d(TAG, "Delivering msg to same user " + debugMsg);
248                return true;
249            }
250            long ident = Binder.clearCallingIdentity();
251            try {
252                UserInfo userInfo = mUm.getUserInfo(user.getIdentifier());
253                UserInfo listeningUserInfo = mUm.getUserInfo(listeningUser.getIdentifier());
254                if (userInfo == null || listeningUserInfo == null
255                        || userInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
256                        || userInfo.profileGroupId != listeningUserInfo.profileGroupId
257                        || !userInfo.isEnabled()) {
258                    if (DEBUG) {
259                        Log.d(TAG, "Not delivering msg from " + user + " to " + listeningUser + ":"
260                                + debugMsg);
261                    }
262                    return false;
263                } else {
264                    if (DEBUG) {
265                        Log.d(TAG, "Delivering msg from " + user + " to " + listeningUser + ":"
266                                + debugMsg);
267                    }
268                    return true;
269                }
270            } finally {
271                Binder.restoreCallingIdentity(ident);
272            }
273        }
274
275        @Override
276        public void onPackageAdded(String packageName, int uid) {
277            UserHandle user = new UserHandle(getChangingUserId());
278            final int n = mListeners.beginBroadcast();
279            for (int i = 0; i < n; i++) {
280                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
281                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
282                if (!isEnabledProfileOf(user, listeningUser, "onPackageAdded")) continue;
283                try {
284                    listener.onPackageAdded(user, packageName);
285                } catch (RemoteException re) {
286                    Slog.d(TAG, "Callback failed ", re);
287                }
288            }
289            mListeners.finishBroadcast();
290
291            super.onPackageAdded(packageName, uid);
292        }
293
294        @Override
295        public void onPackageRemoved(String packageName, int uid) {
296            UserHandle user = new UserHandle(getChangingUserId());
297            final int n = mListeners.beginBroadcast();
298            for (int i = 0; i < n; i++) {
299                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
300                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
301                if (!isEnabledProfileOf(user, listeningUser, "onPackageRemoved")) continue;
302                try {
303                    listener.onPackageRemoved(user, packageName);
304                } catch (RemoteException re) {
305                    Slog.d(TAG, "Callback failed ", re);
306                }
307            }
308            mListeners.finishBroadcast();
309
310            super.onPackageRemoved(packageName, uid);
311        }
312
313        @Override
314        public void onPackageModified(String packageName) {
315            UserHandle user = new UserHandle(getChangingUserId());
316            final int n = mListeners.beginBroadcast();
317            for (int i = 0; i < n; i++) {
318                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
319                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
320                if (!isEnabledProfileOf(user, listeningUser, "onPackageModified")) continue;
321                try {
322                    listener.onPackageChanged(user, packageName);
323                } catch (RemoteException re) {
324                    Slog.d(TAG, "Callback failed ", re);
325                }
326            }
327            mListeners.finishBroadcast();
328
329            super.onPackageModified(packageName);
330        }
331
332        @Override
333        public void onPackagesAvailable(String[] packages) {
334            UserHandle user = new UserHandle(getChangingUserId());
335            final int n = mListeners.beginBroadcast();
336            for (int i = 0; i < n; i++) {
337                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
338                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
339                if (!isEnabledProfileOf(user, listeningUser, "onPackagesAvailable")) continue;
340                try {
341                    listener.onPackagesAvailable(user, packages, isReplacing());
342                } catch (RemoteException re) {
343                    Slog.d(TAG, "Callback failed ", re);
344                }
345            }
346            mListeners.finishBroadcast();
347
348            super.onPackagesAvailable(packages);
349        }
350
351        @Override
352        public void onPackagesUnavailable(String[] packages) {
353            UserHandle user = new UserHandle(getChangingUserId());
354            final int n = mListeners.beginBroadcast();
355            for (int i = 0; i < n; i++) {
356                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
357                UserHandle listeningUser = (UserHandle) mListeners.getBroadcastCookie(i);
358                if (!isEnabledProfileOf(user, listeningUser, "onPackagesUnavailable")) continue;
359                try {
360                    listener.onPackagesUnavailable(user, packages, isReplacing());
361                } catch (RemoteException re) {
362                    Slog.d(TAG, "Callback failed ", re);
363                }
364            }
365            mListeners.finishBroadcast();
366
367            super.onPackagesUnavailable(packages);
368        }
369
370    }
371
372    class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
373        @Override
374        public void onCallbackDied(T callback, Object cookie) {
375            checkCallbackCount();
376        }
377    }
378}
379