TileServices.java revision d5a204f16e7c71ffdbc6c8307a4134dcc1efd60d
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 */
16package com.android.systemui.qs.external;
17
18import android.content.ComponentName;
19import android.content.Context;
20import android.content.pm.PackageManager;
21import android.os.Binder;
22import android.os.Handler;
23import android.os.Looper;
24import android.service.quicksettings.IQSService;
25import android.service.quicksettings.Tile;
26import android.util.ArrayMap;
27import com.android.systemui.statusbar.phone.QSTileHost;
28
29import java.util.ArrayList;
30import java.util.Collections;
31import java.util.Comparator;
32
33/**
34 * Runs the day-to-day operations of which tiles should be bound and when.
35 */
36public class TileServices extends IQSService.Stub {
37    static final int DEFAULT_MAX_BOUND = 3;
38    static final int REDUCED_MAX_BOUND = 1;
39
40    private final ArrayMap<CustomTile, TileServiceManager> mServices = new ArrayMap<>();
41    private final ArrayMap<ComponentName, CustomTile> mTiles = new ArrayMap<>();
42    private final Context mContext;
43    private final Handler mHandler;
44    private final QSTileHost mHost;
45
46    private int mMaxBound = DEFAULT_MAX_BOUND;
47
48    public TileServices(QSTileHost host, Looper looper) {
49        mHost = host;
50        mContext = mHost.getContext();
51        mHandler = new Handler(looper);
52    }
53
54    public Context getContext() {
55        return mContext;
56    }
57
58    public TileServiceManager getTileWrapper(CustomTile tile) {
59        ComponentName component = tile.getComponent();
60        TileServiceManager service = onCreateTileService(component);
61        synchronized (mServices) {
62            mServices.put(tile, service);
63            mTiles.put(component, tile);
64        }
65        return service;
66    }
67
68    protected TileServiceManager onCreateTileService(ComponentName component) {
69        return new TileServiceManager(this, mHandler, component);
70    }
71
72    public void freeService(CustomTile tile, TileServiceManager service) {
73        synchronized (mServices) {
74            service.setBindAllowed(false);
75            mServices.remove(tile);
76            mTiles.remove(tile.getComponent());
77        }
78    }
79
80    public void setMemoryPressure(boolean memoryPressure) {
81        mMaxBound = memoryPressure ? REDUCED_MAX_BOUND : DEFAULT_MAX_BOUND;
82        recalculateBindAllowance();
83    }
84
85    public void recalculateBindAllowance() {
86        final ArrayList<TileServiceManager> services;
87        synchronized (mServices) {
88            services = new ArrayList<>(mServices.values());
89        }
90        final int N = services.size();
91        if (N > mMaxBound) {
92            long currentTime = System.currentTimeMillis();
93            // Precalculate the priority of services for binding.
94            for (int i = 0; i < N; i++) {
95                services.get(i).calculateBindPriority(currentTime);
96            }
97            // Sort them so we can bind the most important first.
98            Collections.sort(services, SERVICE_SORT);
99        }
100        int i;
101        // Allow mMaxBound items to bind.
102        for (i = 0; i < mMaxBound && i < N; i++) {
103            services.get(i).setBindAllowed(true);
104        }
105        // The rest aren't allowed to bind for now.
106        while (i < N) {
107            services.get(i).setBindAllowed(false);
108            i++;
109        }
110    }
111
112    private void verifyCaller(String packageName) {
113        try {
114            int uid = mContext.getPackageManager().getPackageUid(packageName,
115                    Binder.getCallingUserHandle().getIdentifier());
116            if (Binder.getCallingUid() != uid) {
117                throw new SecurityException("Component outside caller's uid");
118            }
119        } catch (PackageManager.NameNotFoundException e) {
120            throw new SecurityException(e);
121        }
122    }
123
124    @Override
125    public void updateQsTile(Tile tile) {
126        verifyCaller(tile.getComponentName().getPackageName());
127        CustomTile customTile = getTileForComponent(tile.getComponentName());
128        if (customTile != null) {
129            mServices.get(customTile).setLastUpdate(System.currentTimeMillis());
130            customTile.updateState(tile);
131            customTile.refreshState();
132        }
133    }
134
135    @Override
136    public void onShowDialog(Tile tile) {
137        verifyCaller(tile.getComponentName().getPackageName());
138        CustomTile customTile = getTileForComponent(tile.getComponentName());
139        if (customTile != null) {
140            customTile.onDialogShown();
141            mHost.collapsePanels();
142        }
143    }
144
145    private CustomTile getTileForComponent(ComponentName component) {
146        return mTiles.get(component);
147    }
148
149    private static final Comparator<TileServiceManager> SERVICE_SORT =
150            new Comparator<TileServiceManager>() {
151        @Override
152        public int compare(TileServiceManager left, TileServiceManager right) {
153            return -Integer.compare(left.getBindPriority(), right.getBindPriority());
154        }
155    };
156}
157