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 android.app;
18
19import android.content.Context;
20import android.os.Handler;
21import android.os.Looper;
22import android.os.Message;
23import android.os.RemoteException;
24import android.util.SparseArray;
25import android.util.SparseIntArray;
26
27import java.util.List;
28
29/**
30 * Helper for monitoring the current importance of applications.
31 * @hide
32 */
33public class AppImportanceMonitor {
34    final Context mContext;
35
36    final SparseArray<AppEntry> mApps = new SparseArray<>();
37
38    static class AppEntry {
39        final int uid;
40        final SparseArray<Integer> procs = new SparseArray<>(1);
41        int importance = ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
42
43        AppEntry(int _uid) {
44            uid = _uid;
45        }
46    }
47
48    final IProcessObserver mProcessObserver = new IProcessObserver.Stub() {
49        @Override
50        public void onForegroundActivitiesChanged(int pid, int uid, boolean foregroundActivities) {
51        }
52
53        @Override
54        public void onProcessStateChanged(int pid, int uid, int procState) {
55            synchronized (mApps) {
56                updateImportanceLocked(pid, uid,
57                        ActivityManager.RunningAppProcessInfo.procStateToImportance(procState),
58                        true);
59            }
60        }
61
62        @Override
63        public void onProcessDied(int pid, int uid) {
64            synchronized (mApps) {
65                updateImportanceLocked(pid, uid,
66                        ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE, true);
67            }
68        }
69    };
70
71    static final int MSG_UPDATE = 1;
72
73    final Handler mHandler;
74
75    public AppImportanceMonitor(Context context, Looper looper) {
76        mContext = context;
77        mHandler = new Handler(looper) {
78            @Override
79            public void handleMessage(Message msg) {
80                switch (msg.what) {
81                    case MSG_UPDATE:
82                        onImportanceChanged(msg.arg1, msg.arg2&0xffff, msg.arg2>>16);
83                        break;
84                    default:
85                        super.handleMessage(msg);
86                }
87            }
88        };
89        ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
90        try {
91            ActivityManagerNative.getDefault().registerProcessObserver(mProcessObserver);
92        } catch (RemoteException e) {
93        }
94        List<ActivityManager.RunningAppProcessInfo> apps = am.getRunningAppProcesses();
95        if (apps != null) {
96            for (int i=0; i<apps.size(); i++) {
97                ActivityManager.RunningAppProcessInfo app = apps.get(i);
98                updateImportanceLocked(app.uid, app.pid, app.importance, false);
99            }
100        }
101    }
102
103    public int getImportance(int uid) {
104        AppEntry ent = mApps.get(uid);
105        if (ent == null) {
106            return ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
107        }
108        return ent.importance;
109    }
110
111    /**
112     * Report when an app's importance changed. Called on looper given to constructor.
113     */
114    public void onImportanceChanged(int uid, int importance, int oldImportance) {
115    }
116
117    void updateImportanceLocked(int uid, int pid, int importance, boolean repChange) {
118        AppEntry ent = mApps.get(uid);
119        if (ent == null) {
120            ent = new AppEntry(uid);
121            mApps.put(uid, ent);
122        }
123        if (importance >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE) {
124            ent.procs.remove(pid);
125        } else {
126            ent.procs.put(pid, importance);
127        }
128        updateImportanceLocked(ent, repChange);
129    }
130
131    void updateImportanceLocked(AppEntry ent, boolean repChange) {
132        int appImp = ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
133        for (int i=0; i<ent.procs.size(); i++) {
134            int procImp = ent.procs.valueAt(i);
135            if (procImp < appImp) {
136                appImp = procImp;
137            }
138        }
139        if (appImp != ent.importance) {
140            int impCode = appImp | (ent.importance<<16);
141            ent.importance = appImp;
142            if (appImp >= ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE) {
143                mApps.remove(ent.uid);
144            }
145            if (repChange) {
146                mHandler.obtainMessage(MSG_UPDATE, ent.uid, impCode).sendToTarget();
147            }
148        }
149    }
150}
151