LauncherAppsService.java revision 1a4e0b998030bb86196559947f417ee6fe117644
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        mainIntent.setPackage(packageName);
148        long ident = Binder.clearCallingIdentity();
149        try {
150            List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0,
151                    user.getIdentifier());
152            return apps;
153        } finally {
154            Binder.restoreCallingIdentity(ident);
155        }
156    }
157
158    @Override
159    public ResolveInfo resolveActivity(Intent intent, UserHandle user)
160            throws RemoteException {
161        ensureInUserProfiles(user, "Cannot resolve activity for unrelated profile " + user);
162
163        long ident = Binder.clearCallingIdentity();
164        try {
165            ResolveInfo app = mPm.resolveActivityAsUser(intent, 0, user.getIdentifier());
166            return app;
167        } finally {
168            Binder.restoreCallingIdentity(ident);
169        }
170    }
171
172    @Override
173    public void startActivityAsUser(ComponentName component, Rect sourceBounds,
174            Bundle opts, UserHandle user) throws RemoteException {
175        ensureInUserProfiles(user, "Cannot start activity for unrelated profile " + user);
176
177        Intent launchIntent = new Intent(Intent.ACTION_MAIN);
178        launchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
179        launchIntent.setComponent(component);
180        launchIntent.setSourceBounds(sourceBounds);
181        launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
182
183        final int callingUserId = UserHandle.getCallingUserId();
184        long ident = Binder.clearCallingIdentity();
185        try {
186            mContext.startActivityAsUser(launchIntent, opts, user);
187        } finally {
188            Binder.restoreCallingIdentity(ident);
189        }
190    }
191
192    private class MyPackageMonitor extends PackageMonitor {
193
194        @Override
195        public void onPackageAdded(String packageName, int uid) {
196            UserHandle user = new UserHandle(getChangingUserId());
197            // TODO: if (!isProfile(user)) return;
198            final int n = mListeners.beginBroadcast();
199            for (int i = 0; i < n; i++) {
200                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
201                try {
202                    listener.onPackageAdded(user, packageName);
203                } catch (RemoteException re) {
204                    Slog.d(TAG, "Callback failed ", re);
205                }
206            }
207            mListeners.finishBroadcast();
208
209            super.onPackageAdded(packageName, uid);
210        }
211
212        @Override
213        public void onPackageRemoved(String packageName, int uid) {
214            UserHandle user = new UserHandle(getChangingUserId());
215            // TODO: if (!isCurrentProfile(user)) return;
216            final int n = mListeners.beginBroadcast();
217            for (int i = 0; i < n; i++) {
218                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
219                try {
220                    listener.onPackageRemoved(user, packageName);
221                } catch (RemoteException re) {
222                    Slog.d(TAG, "Callback failed ", re);
223                }
224            }
225            mListeners.finishBroadcast();
226
227            super.onPackageRemoved(packageName, uid);
228        }
229
230        @Override
231        public void onPackageModified(String packageName) {
232            UserHandle user = new UserHandle(getChangingUserId());
233            // TODO: if (!isProfile(user)) return;
234            final int n = mListeners.beginBroadcast();
235            for (int i = 0; i < n; i++) {
236                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
237                try {
238                    listener.onPackageChanged(user, packageName);
239                } catch (RemoteException re) {
240                    Slog.d(TAG, "Callback failed ", re);
241                }
242            }
243            mListeners.finishBroadcast();
244
245            super.onPackageModified(packageName);
246        }
247
248        @Override
249        public void onPackagesAvailable(String[] packages) {
250            UserHandle user = new UserHandle(getChangingUserId());
251            // TODO: if (!isProfile(user)) return;
252            final int n = mListeners.beginBroadcast();
253            for (int i = 0; i < n; i++) {
254                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
255                try {
256                    listener.onPackagesAvailable(user, packages, isReplacing());
257                } catch (RemoteException re) {
258                    Slog.d(TAG, "Callback failed ", re);
259                }
260            }
261            mListeners.finishBroadcast();
262
263            super.onPackagesAvailable(packages);
264        }
265
266        @Override
267        public void onPackagesUnavailable(String[] packages) {
268            UserHandle user = new UserHandle(getChangingUserId());
269            // TODO: if (!isProfile(user)) return;
270            final int n = mListeners.beginBroadcast();
271            for (int i = 0; i < n; i++) {
272                IOnAppsChangedListener listener = mListeners.getBroadcastItem(i);
273                try {
274                    listener.onPackagesUnavailable(user, packages, isReplacing());
275                } catch (RemoteException re) {
276                    Slog.d(TAG, "Callback failed ", re);
277                }
278            }
279            mListeners.finishBroadcast();
280
281            super.onPackagesUnavailable(packages);
282        }
283
284    }
285
286    class PackageCallbackList<T extends IInterface> extends RemoteCallbackList<T> {
287
288        @Override
289        public void onCallbackDied(T callback, Object cookie) {
290            checkCallbackCount();
291        }
292    }
293}
294