• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 package com.android.systemui.qs.external;
17 
18 import android.content.BroadcastReceiver;
19 import android.content.ComponentName;
20 import android.content.Context;
21 import android.content.Intent;
22 import android.content.IntentFilter;
23 import android.content.pm.PackageInfo;
24 import android.content.pm.PackageManager;
25 import android.graphics.drawable.Icon;
26 import android.os.Binder;
27 import android.os.Handler;
28 import android.os.IBinder;
29 import android.os.Looper;
30 import android.os.RemoteException;
31 import android.os.UserHandle;
32 import android.service.quicksettings.IQSService;
33 import android.service.quicksettings.IQSTileService;
34 import android.service.quicksettings.Tile;
35 import android.service.quicksettings.TileService;
36 import android.util.ArrayMap;
37 import android.util.Log;
38 
39 import com.android.internal.statusbar.StatusBarIcon;
40 import com.android.systemui.statusbar.phone.QSTileHost;
41 import com.android.systemui.statusbar.phone.StatusBarIconController;
42 import com.android.systemui.statusbar.policy.KeyguardMonitor;
43 
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.Comparator;
47 
48 /**
49  * Runs the day-to-day operations of which tiles should be bound and when.
50  */
51 public class TileServices extends IQSService.Stub {
52     static final int DEFAULT_MAX_BOUND = 3;
53     static final int REDUCED_MAX_BOUND = 1;
54 
55     private final ArrayMap<CustomTile, TileServiceManager> mServices = new ArrayMap<>();
56     private final ArrayMap<ComponentName, CustomTile> mTiles = new ArrayMap<>();
57     private final ArrayMap<IBinder, CustomTile> mTokenMap = new ArrayMap<>();
58     private final Context mContext;
59     private final Handler mHandler;
60     private final Handler mMainHandler;
61     private final QSTileHost mHost;
62 
63     private int mMaxBound = DEFAULT_MAX_BOUND;
64 
TileServices(QSTileHost host, Looper looper)65     public TileServices(QSTileHost host, Looper looper) {
66         mHost = host;
67         mContext = mHost.getContext();
68         mContext.registerReceiver(mRequestListeningReceiver,
69                 new IntentFilter(TileService.ACTION_REQUEST_LISTENING));
70         mHandler = new Handler(looper);
71         mMainHandler = new Handler(Looper.getMainLooper());
72     }
73 
getContext()74     public Context getContext() {
75         return mContext;
76     }
77 
getHost()78     public QSTileHost getHost() {
79         return mHost;
80     }
81 
getTileWrapper(CustomTile tile)82     public TileServiceManager getTileWrapper(CustomTile tile) {
83         ComponentName component = tile.getComponent();
84         TileServiceManager service = onCreateTileService(component, tile.getQsTile());
85         synchronized (mServices) {
86             mServices.put(tile, service);
87             mTiles.put(component, tile);
88             mTokenMap.put(service.getToken(), tile);
89         }
90         return service;
91     }
92 
onCreateTileService(ComponentName component, Tile tile)93     protected TileServiceManager onCreateTileService(ComponentName component, Tile tile) {
94         return new TileServiceManager(this, mHandler, component, tile);
95     }
96 
freeService(CustomTile tile, TileServiceManager service)97     public void freeService(CustomTile tile, TileServiceManager service) {
98         synchronized (mServices) {
99             service.setBindAllowed(false);
100             service.handleDestroy();
101             mServices.remove(tile);
102             mTokenMap.remove(service.getToken());
103             mTiles.remove(tile.getComponent());
104             final String slot = tile.getComponent().getClassName();
105             mMainHandler.post(new Runnable() {
106                 @Override
107                 public void run() {
108                     mHost.getIconController().removeIcon(slot);
109                 }
110             });
111         }
112     }
113 
setMemoryPressure(boolean memoryPressure)114     public void setMemoryPressure(boolean memoryPressure) {
115         mMaxBound = memoryPressure ? REDUCED_MAX_BOUND : DEFAULT_MAX_BOUND;
116         recalculateBindAllowance();
117     }
118 
recalculateBindAllowance()119     public void recalculateBindAllowance() {
120         final ArrayList<TileServiceManager> services;
121         synchronized (mServices) {
122             services = new ArrayList<>(mServices.values());
123         }
124         final int N = services.size();
125         if (N > mMaxBound) {
126             long currentTime = System.currentTimeMillis();
127             // Precalculate the priority of services for binding.
128             for (int i = 0; i < N; i++) {
129                 services.get(i).calculateBindPriority(currentTime);
130             }
131             // Sort them so we can bind the most important first.
132             Collections.sort(services, SERVICE_SORT);
133         }
134         int i;
135         // Allow mMaxBound items to bind.
136         for (i = 0; i < mMaxBound && i < N; i++) {
137             services.get(i).setBindAllowed(true);
138         }
139         // The rest aren't allowed to bind for now.
140         while (i < N) {
141             services.get(i).setBindAllowed(false);
142             i++;
143         }
144     }
145 
verifyCaller(CustomTile tile)146     private void verifyCaller(CustomTile tile) {
147         try {
148             String packageName = tile.getComponent().getPackageName();
149             int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
150                     Binder.getCallingUserHandle().getIdentifier());
151             if (Binder.getCallingUid() != uid) {
152                 throw new SecurityException("Component outside caller's uid");
153             }
154         } catch (PackageManager.NameNotFoundException e) {
155             throw new SecurityException(e);
156         }
157     }
158 
requestListening(ComponentName component)159     private void requestListening(ComponentName component) {
160         synchronized (mServices) {
161             CustomTile customTile = getTileForComponent(component);
162             if (customTile == null) {
163                 Log.d("TileServices", "Couldn't find tile for " + component);
164                 return;
165             }
166             TileServiceManager service = mServices.get(customTile);
167             if (!service.isActiveTile()) {
168                 return;
169             }
170             service.setBindRequested(true);
171             try {
172                 service.getTileService().onStartListening();
173             } catch (RemoteException e) {
174             }
175         }
176     }
177 
178     @Override
updateQsTile(Tile tile, IBinder token)179     public void updateQsTile(Tile tile, IBinder token) {
180         CustomTile customTile = getTileForToken(token);
181         if (customTile != null) {
182             verifyCaller(customTile);
183             synchronized (mServices) {
184                 final TileServiceManager tileServiceManager = mServices.get(customTile);
185                 tileServiceManager.clearPendingBind();
186                 tileServiceManager.setLastUpdate(System.currentTimeMillis());
187             }
188             customTile.updateState(tile);
189             customTile.refreshState();
190         }
191     }
192 
193     @Override
onStartSuccessful(IBinder token)194     public void onStartSuccessful(IBinder token) {
195         CustomTile customTile = getTileForToken(token);
196         if (customTile != null) {
197             verifyCaller(customTile);
198             synchronized (mServices) {
199                 final TileServiceManager tileServiceManager = mServices.get(customTile);
200                 tileServiceManager.clearPendingBind();
201             }
202             customTile.refreshState();
203         }
204     }
205 
206     @Override
onShowDialog(IBinder token)207     public void onShowDialog(IBinder token) {
208         CustomTile customTile = getTileForToken(token);
209         if (customTile != null) {
210             verifyCaller(customTile);
211             customTile.onDialogShown();
212             mHost.collapsePanels();
213             mServices.get(customTile).setShowingDialog(true);
214         }
215     }
216 
217     @Override
onDialogHidden(IBinder token)218     public void onDialogHidden(IBinder token) {
219         CustomTile customTile = getTileForToken(token);
220         if (customTile != null) {
221             verifyCaller(customTile);
222             mServices.get(customTile).setShowingDialog(false);
223             customTile.onDialogHidden();
224         }
225     }
226 
227     @Override
onStartActivity(IBinder token)228     public void onStartActivity(IBinder token) {
229         CustomTile customTile = getTileForToken(token);
230         if (customTile != null) {
231             verifyCaller(customTile);
232             mHost.collapsePanels();
233         }
234     }
235 
236     @Override
updateStatusIcon(IBinder token, Icon icon, String contentDescription)237     public void updateStatusIcon(IBinder token, Icon icon, String contentDescription) {
238         CustomTile customTile = getTileForToken(token);
239         if (customTile != null) {
240             verifyCaller(customTile);
241             try {
242                 ComponentName componentName = customTile.getComponent();
243                 String packageName = componentName.getPackageName();
244                 UserHandle userHandle = getCallingUserHandle();
245                 PackageInfo info = mContext.getPackageManager().getPackageInfoAsUser(packageName, 0,
246                         userHandle.getIdentifier());
247                 if (info.applicationInfo.isSystemApp()) {
248                     final StatusBarIcon statusIcon = icon != null
249                             ? new StatusBarIcon(userHandle, packageName, icon, 0, 0,
250                                     contentDescription)
251                             : null;
252                     mMainHandler.post(new Runnable() {
253                         @Override
254                         public void run() {
255                             StatusBarIconController iconController = mHost.getIconController();
256                             iconController.setIcon(componentName.getClassName(), statusIcon);
257                             iconController.setExternalIcon(componentName.getClassName());
258                         }
259                     });
260                 }
261             } catch (PackageManager.NameNotFoundException e) {
262             }
263         }
264     }
265 
266     @Override
getTile(IBinder token)267     public Tile getTile(IBinder token) {
268         CustomTile customTile = getTileForToken(token);
269         if (customTile != null) {
270             verifyCaller(customTile);
271             return customTile.getQsTile();
272         }
273         return null;
274     }
275 
276     @Override
startUnlockAndRun(IBinder token)277     public void startUnlockAndRun(IBinder token) {
278         CustomTile customTile = getTileForToken(token);
279         if (customTile != null) {
280             verifyCaller(customTile);
281             customTile.startUnlockAndRun();
282         }
283     }
284 
285     @Override
isLocked()286     public boolean isLocked() {
287         KeyguardMonitor keyguardMonitor = mHost.getKeyguardMonitor();
288         return keyguardMonitor.isShowing();
289     }
290 
291     @Override
isSecure()292     public boolean isSecure() {
293         KeyguardMonitor keyguardMonitor = mHost.getKeyguardMonitor();
294         return keyguardMonitor.isSecure() && keyguardMonitor.isShowing();
295     }
296 
getTileForToken(IBinder token)297     private CustomTile getTileForToken(IBinder token) {
298         synchronized (mServices) {
299             return mTokenMap.get(token);
300         }
301     }
302 
getTileForComponent(ComponentName component)303     private CustomTile getTileForComponent(ComponentName component) {
304         synchronized (mServices) {
305             return mTiles.get(component);
306         }
307     }
308 
309     private final BroadcastReceiver mRequestListeningReceiver = new BroadcastReceiver() {
310         @Override
311         public void onReceive(Context context, Intent intent) {
312             if (TileService.ACTION_REQUEST_LISTENING.equals(intent.getAction())) {
313                 requestListening(
314                         (ComponentName) intent.getParcelableExtra(TileService.EXTRA_COMPONENT));
315             }
316         }
317     };
318 
319     private static final Comparator<TileServiceManager> SERVICE_SORT =
320             new Comparator<TileServiceManager>() {
321         @Override
322         public int compare(TileServiceManager left, TileServiceManager right) {
323             return -Integer.compare(left.getBindPriority(), right.getBindPriority());
324         }
325     };
326 }
327