TileServices.java revision 66c89c15a0baabf001e69498dbc09903f72cc63d
1d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk/*
2d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk * Copyright (C) 2015 The Android Open Source Project
3d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk *
4d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk * Licensed under the Apache License, Version 2.0 (the "License");
5d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk * you may not use this file except in compliance with the License.
6d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk * You may obtain a copy of the License at
7d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk *
8d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk *      http://www.apache.org/licenses/LICENSE-2.0
9d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk *
10d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk * Unless required by applicable law or agreed to in writing, software
11d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk * distributed under the License is distributed on an "AS IS" BASIS,
12d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk * See the License for the specific language governing permissions and
14d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk * limitations under the License
15d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk */
16d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkpackage com.android.systemui.qs.external;
17d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
18fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monkimport android.content.BroadcastReceiver;
19d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport android.content.ComponentName;
20d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport android.content.Context;
21fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monkimport android.content.Intent;
22fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monkimport android.content.IntentFilter;
2366c89c15a0baabf001e69498dbc09903f72cc63dJason Monkimport android.content.pm.PackageInfo;
24d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport android.content.pm.PackageManager;
2566c89c15a0baabf001e69498dbc09903f72cc63dJason Monkimport android.graphics.drawable.Icon;
26d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport android.os.Binder;
27d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport android.os.Handler;
28d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport android.os.Looper;
29fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monkimport android.os.RemoteException;
3066c89c15a0baabf001e69498dbc09903f72cc63dJason Monkimport android.os.UserHandle;
31d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport android.service.quicksettings.IQSService;
32d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport android.service.quicksettings.Tile;
33fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monkimport android.service.quicksettings.TileService;
34d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport android.util.ArrayMap;
35fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monkimport android.util.Log;
3666c89c15a0baabf001e69498dbc09903f72cc63dJason Monkimport com.android.internal.statusbar.StatusBarIcon;
37d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport com.android.systemui.statusbar.phone.QSTileHost;
3866c89c15a0baabf001e69498dbc09903f72cc63dJason Monkimport com.android.systemui.statusbar.phone.StatusBarIconController;
39d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
40d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport java.util.ArrayList;
41d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport java.util.Collections;
42d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkimport java.util.Comparator;
43d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
44d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk/**
45d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk * Runs the day-to-day operations of which tiles should be bound and when.
46d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk */
47d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monkpublic class TileServices extends IQSService.Stub {
48d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    static final int DEFAULT_MAX_BOUND = 3;
49d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    static final int REDUCED_MAX_BOUND = 1;
50d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
51d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    private final ArrayMap<CustomTile, TileServiceManager> mServices = new ArrayMap<>();
52d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    private final ArrayMap<ComponentName, CustomTile> mTiles = new ArrayMap<>();
53d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    private final Context mContext;
54d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    private final Handler mHandler;
5566c89c15a0baabf001e69498dbc09903f72cc63dJason Monk    private final Handler mMainHandler;
56d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    private final QSTileHost mHost;
57d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
58d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    private int mMaxBound = DEFAULT_MAX_BOUND;
59d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
60d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    public TileServices(QSTileHost host, Looper looper) {
61d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        mHost = host;
62d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        mContext = mHost.getContext();
63fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        mContext.registerReceiver(mRequestListeningReceiver,
64fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk                new IntentFilter(TileService.ACTION_REQUEST_LISTENING));
65d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        mHandler = new Handler(looper);
6666c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        mMainHandler = new Handler(Looper.getMainLooper());
67d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    }
68d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
69d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    public Context getContext() {
70d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        return mContext;
71d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    }
72d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
73d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    public TileServiceManager getTileWrapper(CustomTile tile) {
74d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        ComponentName component = tile.getComponent();
75d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        TileServiceManager service = onCreateTileService(component);
76d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        synchronized (mServices) {
77d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            mServices.put(tile, service);
78d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            mTiles.put(component, tile);
79d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        }
80d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        return service;
81d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    }
82d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
83d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    protected TileServiceManager onCreateTileService(ComponentName component) {
84d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        return new TileServiceManager(this, mHandler, component);
85d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    }
86d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
87d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    public void freeService(CustomTile tile, TileServiceManager service) {
88d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        synchronized (mServices) {
89d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            service.setBindAllowed(false);
90d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            mServices.remove(tile);
91d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            mTiles.remove(tile.getComponent());
9266c89c15a0baabf001e69498dbc09903f72cc63dJason Monk            final String slot = tile.getComponent().getClassName();
9366c89c15a0baabf001e69498dbc09903f72cc63dJason Monk            mMainHandler.post(new Runnable() {
9466c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                @Override
9566c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                public void run() {
9666c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                    mHost.getIconController().removeIcon(slot);
9766c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                }
9866c89c15a0baabf001e69498dbc09903f72cc63dJason Monk            });
99d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        }
100d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    }
101d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
102d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    public void setMemoryPressure(boolean memoryPressure) {
103d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        mMaxBound = memoryPressure ? REDUCED_MAX_BOUND : DEFAULT_MAX_BOUND;
104d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        recalculateBindAllowance();
105d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    }
106d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
107d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    public void recalculateBindAllowance() {
108d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        final ArrayList<TileServiceManager> services;
109d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        synchronized (mServices) {
110d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            services = new ArrayList<>(mServices.values());
111d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        }
112d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        final int N = services.size();
113d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        if (N > mMaxBound) {
114d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            long currentTime = System.currentTimeMillis();
115d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            // Precalculate the priority of services for binding.
116d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            for (int i = 0; i < N; i++) {
117d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk                services.get(i).calculateBindPriority(currentTime);
118d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            }
119d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            // Sort them so we can bind the most important first.
120d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            Collections.sort(services, SERVICE_SORT);
121d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        }
122d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        int i;
123d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        // Allow mMaxBound items to bind.
124d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        for (i = 0; i < mMaxBound && i < N; i++) {
125d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            services.get(i).setBindAllowed(true);
126d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        }
127d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        // The rest aren't allowed to bind for now.
128d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        while (i < N) {
129d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            services.get(i).setBindAllowed(false);
130d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            i++;
131d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        }
132d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    }
133d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
134d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    private void verifyCaller(String packageName) {
135d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        try {
136e06b4d1d9f718b9fe02980fea794a36831a16db2Jeff Sharkey            int uid = mContext.getPackageManager().getPackageUidAsUser(packageName,
137d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk                    Binder.getCallingUserHandle().getIdentifier());
138d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            if (Binder.getCallingUid() != uid) {
139d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk                throw new SecurityException("Component outside caller's uid");
140d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            }
141d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        } catch (PackageManager.NameNotFoundException e) {
142d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            throw new SecurityException(e);
143d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        }
144d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    }
145d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
146fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk    private void requestListening(ComponentName component) {
147fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        synchronized (mServices) {
148fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            CustomTile customTile = getTileForComponent(component);
149fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            if (customTile == null) {
150fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk                Log.d("TileServices", "Couldn't find tile for " + component);
151fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk                return;
152fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            }
153fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            TileServiceManager service = mServices.get(customTile);
154fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            if (service.getType() != TileService.TILE_MODE_ACTIVE) {
155fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk                return;
156fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            }
157fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            service.setBindRequested(true);
158fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            try {
159fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk                service.getTileService().onStartListening();
160fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            } catch (RemoteException e) {
161fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            }
162fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        }
163fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk    }
164fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk
165fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk    @Override
166fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk    public void setTileMode(ComponentName component, int mode) {
167fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        verifyCaller(component.getPackageName());
168fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        CustomTile customTile = getTileForComponent(component);
169fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        if (customTile != null) {
170fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            synchronized (mServices) {
171fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk                mServices.get(customTile).setType(mode);
172fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            }
173fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        }
174fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk    }
175fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk
176d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    @Override
177d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    public void updateQsTile(Tile tile) {
17866c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        ComponentName componentName = tile.getComponentName();
17966c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        verifyCaller(componentName.getPackageName());
18066c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        CustomTile customTile = getTileForComponent(componentName);
181d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        if (customTile != null) {
182fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            synchronized (mServices) {
183fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk                mServices.get(customTile).setLastUpdate(System.currentTimeMillis());
184fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            }
185d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            customTile.updateState(tile);
186d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            customTile.refreshState();
187d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        }
188d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    }
189d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
190d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    @Override
191d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    public void onShowDialog(Tile tile) {
19266c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        ComponentName componentName = tile.getComponentName();
19366c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        verifyCaller(componentName.getPackageName());
19466c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        CustomTile customTile = getTileForComponent(componentName);
195d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        if (customTile != null) {
196d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            customTile.onDialogShown();
197d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            mHost.collapsePanels();
198d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        }
199d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    }
200d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
20166c89c15a0baabf001e69498dbc09903f72cc63dJason Monk    @Override
20266c89c15a0baabf001e69498dbc09903f72cc63dJason Monk    public void updateStatusIcon(Tile tile, Icon icon, String contentDescription) {
20366c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        final ComponentName componentName = tile.getComponentName();
20466c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        String packageName = componentName.getPackageName();
20566c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        verifyCaller(packageName);
20666c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        CustomTile customTile = getTileForComponent(componentName);
20766c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        if (customTile != null) {
20866c89c15a0baabf001e69498dbc09903f72cc63dJason Monk            try {
20966c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                UserHandle userHandle = getCallingUserHandle();
21066c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                PackageInfo info = mContext.getPackageManager().getPackageInfoAsUser(packageName, 0,
21166c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                        userHandle.getIdentifier());
21266c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                if (info.applicationInfo.isSystemApp()) {
21366c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                    final StatusBarIcon statusIcon = icon != null
21466c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                            ? new StatusBarIcon(userHandle, packageName, icon, 0, 0,
21566c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                                    contentDescription)
21666c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                            : null;
21766c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                    mMainHandler.post(new Runnable() {
21866c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                        @Override
21966c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                        public void run() {
22066c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                            StatusBarIconController iconController = mHost.getIconController();
22166c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                            iconController.setIcon(componentName.getClassName(), statusIcon);
22266c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                            iconController.setExternalIcon(componentName.getClassName());
22366c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                        }
22466c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                    });
22566c89c15a0baabf001e69498dbc09903f72cc63dJason Monk                }
22666c89c15a0baabf001e69498dbc09903f72cc63dJason Monk            } catch (PackageManager.NameNotFoundException e) {
22766c89c15a0baabf001e69498dbc09903f72cc63dJason Monk            }
22866c89c15a0baabf001e69498dbc09903f72cc63dJason Monk        }
22966c89c15a0baabf001e69498dbc09903f72cc63dJason Monk    }
23066c89c15a0baabf001e69498dbc09903f72cc63dJason Monk
231d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    private CustomTile getTileForComponent(ComponentName component) {
232fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        synchronized (mServices) {
233fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            return mTiles.get(component);
234fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        }
235d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    }
236d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk
237fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk    private final BroadcastReceiver mRequestListeningReceiver = new BroadcastReceiver() {
238fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        @Override
239fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        public void onReceive(Context context, Intent intent) {
240fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            if (TileService.ACTION_REQUEST_LISTENING.equals(intent.getAction())) {
241fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk                requestListening(
242fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk                        (ComponentName) intent.getParcelableExtra(TileService.EXTRA_COMPONENT));
243fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk            }
244fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk        }
245fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk    };
246fe8f6826ce3c2beeb1fce54c67978ce69f849407Jason Monk
247d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    private static final Comparator<TileServiceManager> SERVICE_SORT =
248d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            new Comparator<TileServiceManager>() {
249d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        @Override
250d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        public int compare(TileServiceManager left, TileServiceManager right) {
251d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk            return -Integer.compare(left.getBindPriority(), right.getBindPriority());
252d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk        }
253d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk    };
254d5a204f16e7c71ffdbc6c8307a4134dcc1efd60dJason Monk}
255