LauncherAppsService.java revision 4f58263d02f296430a9653126d28501e95c7bb6c
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.content.BroadcastReceiver;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.content.pm.ILauncherApps;
25import android.content.pm.IOnAppsChangedListener;
26import android.content.pm.PackageManager;
27import android.content.pm.ResolveInfo;
28import android.content.pm.UserInfo;
29import android.graphics.Rect;
30import android.os.Binder;
31import android.os.Bundle;
32import android.os.IInterface;
33import android.os.RemoteCallbackList;
34import android.os.RemoteException;
35import android.os.UserHandle;
36import android.os.UserManager;
37import android.util.Slog;
38
39import com.android.internal.content.PackageMonitor;
40
41import java.util.ArrayList;
42import java.util.List;
43
44/**
45 * Service that manages requests and callbacks for launchers that support
46 * managed profiles.
47 */
48public class LauncherAppsService extends ILauncherApps.Stub {
49
50    private static final String TAG = "LauncherAppsService";
51    private final Context mContext;
52    private final PackageManager mPm;
53    private final UserManager mUm;
54    private final PackageCallbackList<IOnAppsChangedListener> mListeners
55            = new PackageCallbackList<IOnAppsChangedListener>();
56
57    private MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
58
59    public LauncherAppsService(Context context) {
60        mContext = context;
61        mPm = mContext.getPackageManager();
62        mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
63    }
64
65    /*
66     * @see android.content.pm.ILauncherApps#addOnAppsChangedListener(
67     *          android.content.pm.IOnAppsChangedListener)
68     */
69    @Override
70    public void addOnAppsChangedListener(IOnAppsChangedListener listener) throws RemoteException {
71        synchronized (mListeners) {
72            if (mListeners.getRegisteredCallbackCount() == 0) {
73                startWatchingPackageBroadcasts();
74            }
75            mListeners.unregister(listener);
76            mListeners.register(listener);
77        }
78    }
79
80    /*
81     * @see android.content.pm.ILauncherApps#removeOnAppsChangedListener(
82     *          android.content.pm.IOnAppsChangedListener)
83     */
84    @Override
85    public void removeOnAppsChangedListener(IOnAppsChangedListener listener)
86            throws RemoteException {
87        synchronized (mListeners) {
88            mListeners.unregister(listener);
89            if (mListeners.getRegisteredCallbackCount() == 0) {
90                stopWatchingPackageBroadcasts();
91            }
92        }
93    }
94
95    /**
96     * Register a receiver to watch for package broadcasts
97     */
98    private void startWatchingPackageBroadcasts() {
99        mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
100    }
101
102    /**
103     * Unregister package broadcast receiver
104     */
105    private void stopWatchingPackageBroadcasts() {
106        mPackageMonitor.unregister();
107    }
108
109    void checkCallbackCount() {
110        synchronized (LauncherAppsService.this) {
111            if (mListeners.getRegisteredCallbackCount() == 0) {
112                stopWatchingPackageBroadcasts();
113            }
114        }
115    }
116
117    /**
118     * Checks if the caller is in the same group as the userToCheck.
119     */
120    private void ensureInUserProfiles(UserHandle userToCheck, String message) {
121        final int callingUserId = UserHandle.getCallingUserId();
122        final int targetUserId = userToCheck.getIdentifier();
123
124        if (targetUserId == callingUserId) return;
125
126        long ident = Binder.clearCallingIdentity();
127        try {
128            UserInfo callingUserInfo = mUm.getUserInfo(callingUserId);
129            UserInfo targetUserInfo = mUm.getUserInfo(targetUserId);
130            if (targetUserInfo == null
131                    || targetUserInfo.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID
132                    || targetUserInfo.profileGroupId != callingUserInfo.profileGroupId) {
133                throw new SecurityException(message);
134            }
135        } finally {
136            Binder.restoreCallingIdentity(ident);
137        }
138    }
139
140    @Override
141    public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
142            throws RemoteException {
143        ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
144
145        final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
146        mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
147        long ident = Binder.clearCallingIdentity();
148        try {
149            List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0,
150                    user.getIdentifier());
151            return apps;
152        } finally {
153            Binder.restoreCallingIdentity(ident);
154        }
155    }
156
157    @Override
158    public ResolveInfo resolveActivity(Intent intent, UserHandle user)
159            throws RemoteException {
160        ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
161
162        long ident = Binder.clearCallingIdentity();
163        try {
164            ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier());
165            return app;
166        } finally {
167            Binder.restoreCallingIdentity(ident);
168        }
169    }
170
171    @Override
172    public void startActivityAsUser(ComponentName component, Rect sourceBounds,
173            Bundle opts, UserHandle user) throws RemoteException {
174        ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
175
176        Intent launchIntent = new Intent(Intent.ACTION_MAIN);
177        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
178        launchIntent.setComponent(component);
179        launchIntent.setSourceBounds(sourceBounds);
180        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
181
182        final int callingUserId = UserHandle.getCallingUserId();
183        long ident = Binder.clearCallingIdentity();
184        try {
185            mContext.startActivityAsUser(launchIntent, opts, user);
186        } finally {
187            Binder.restoreCallingIdentity(ident);
188        }
189    }
190
191    private class MyPackageMonitor extends PackageMonitor {
192
193        @Override
194        public void onPackageAdded(String packageName, int uid) {
195            UserHandle user = new UserHandle(getChangingUserId());
196            // TODO: if (!isProfile(user)) return;
197            final int n = mListeners.beginBroadcast();
198            for (int i = 0; i < n; i++) {
199                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
200                try {
201                    listener.onPackageAdded(user, packageName);
202                } catch (RemoteException re) {
203                    Slog.d(TAG, "Callback failed ", re);
204                }
205            }
206            mListeners.finishBroadcast();
207
208            super.onPackageAdded(packageName, uid);
209        }
210
211        @Override
212        public void onPackageRemoved(String packageName, int uid) {
213            UserHandle user = new UserHandle(getChangingUserId());
214            // TODO: if (!isCurrentProfile(user)) return;
215            final int n = mListeners.beginBroadcast();
216            for (int i = 0; i < n; i++) {
217                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
218                try {
219                    listener.onPackageRemoved(user, packageName);
220                } catch (RemoteException re) {
221                    Slog.d(TAG, "Callback failed ", re);
222                }
223            }
224            mListeners.finishBroadcast();
225
226            super.onPackageRemoved(packageName, uid);
227        }
228
229        @Override
230        public void onPackageModified(String packageName) {
231            UserHandle user = new UserHandle(getChangingUserId());
232            // TODO: if (!isProfile(user)) return;
233            final int n = mListeners.beginBroadcast();
234            for (int i = 0; i < n; i++) {
235                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
236                try {
237                    listener.onPackageChanged(user, packageName);
238                } catch (RemoteException re) {
239                    Slog.d(TAG, "Callback failed ", re);
240                }
241            }
242            mListeners.finishBroadcast();
243
244            super.onPackageModified(packageName);
245        }
246
247        @Override
248        public void onPackagesAvailable(String[] packages) {
249            UserHandle user = new UserHandle(getChangingUserId());
250            // TODO: if (!isProfile(user)) return;
251            final int n = mListeners.beginBroadcast();
252            for (int i = 0; i < n; i++) {
253                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
254                try {
255                    listener.onPackagesAvailable(user, packages, isReplacing());
256                } catch (RemoteException re) {
257                    Slog.d(TAG, "Callback failed ", re);
258                }
259            }
260            mListeners.finishBroadcast();
261
262            super.onPackagesAvailable(packages);
263        }
264
265        @Override
266        public void onPackagesUnavailable(String[] packages) {
267            UserHandle user = new UserHandle(getChangingUserId());
268            // TODO: if (!isProfile(user)) return;
269            final int n = mListeners.beginBroadcast();
270            for (int i = 0; i < n; i++) {
271                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
272                try {
273                    listener.onPackagesUnavailable(user, packages, isReplacing());
274                } catch (RemoteException re) {
275                    Slog.d(TAG, "Callback failed ", re);
276                }
277            }
278            mListeners.finishBroadcast();
279
280            super.onPackagesUnavailable(packages);
281        }
282
283    }
284
285    class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
286
287        @Override
288        public void onCallbackDied(T callback, Object cookie) {
289            checkCallbackCount();
290        }
291    }
292}
293