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
17package com.android.server.locksettings;
18
19import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_NOT_REQUIRED;
20import static com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_TIMEOUT;
21
22import com.android.internal.widget.LockPatternUtils;
23import com.android.internal.widget.LockPatternUtils.StrongAuthTracker;
24
25import android.app.AlarmManager;
26import android.app.AlarmManager.OnAlarmListener;
27import android.app.admin.DevicePolicyManager;
28import android.app.trust.IStrongAuthTracker;
29import android.content.Context;
30import android.content.pm.PackageManager;
31import android.hardware.fingerprint.FingerprintManager;
32import android.os.Binder;
33import android.os.DeadObjectException;
34import android.os.Handler;
35import android.os.Message;
36import android.os.RemoteException;
37import android.os.SystemClock;
38import android.os.UserHandle;
39import android.util.ArrayMap;
40import android.util.Slog;
41import android.util.SparseIntArray;
42
43import java.util.ArrayList;
44
45/**
46 * Keeps track of requests for strong authentication.
47 */
48public class LockSettingsStrongAuth {
49
50    private static final String TAG = "LockSettings";
51
52    private static final int MSG_REQUIRE_STRONG_AUTH = 1;
53    private static final int MSG_REGISTER_TRACKER = 2;
54    private static final int MSG_UNREGISTER_TRACKER = 3;
55    private static final int MSG_REMOVE_USER = 4;
56    private static final int MSG_SCHEDULE_STRONG_AUTH_TIMEOUT = 5;
57
58    private static final String STRONG_AUTH_TIMEOUT_ALARM_TAG =
59            "LockSettingsStrongAuth.timeoutForUser";
60
61    private final ArrayList<IStrongAuthTracker> mStrongAuthTrackers = new ArrayList<>();
62    private final SparseIntArray mStrongAuthForUser = new SparseIntArray();
63    private final ArrayMap<Integer, StrongAuthTimeoutAlarmListener>
64            mStrongAuthTimeoutAlarmListenerForUser = new ArrayMap<>();
65    private final int mDefaultStrongAuthFlags;
66    private final Context mContext;
67
68    private AlarmManager mAlarmManager;
69    private FingerprintManager mFingerprintManager;
70
71    public LockSettingsStrongAuth(Context context) {
72        mContext = context;
73        mDefaultStrongAuthFlags = StrongAuthTracker.getDefaultFlags(context);
74        mAlarmManager = context.getSystemService(AlarmManager.class);
75    }
76
77    public void systemReady() {
78        final PackageManager pm = mContext.getPackageManager();
79        if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
80            mFingerprintManager = mContext.getSystemService(FingerprintManager.class);
81        }
82    }
83
84    private void handleAddStrongAuthTracker(IStrongAuthTracker tracker) {
85        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
86            if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
87                return;
88            }
89        }
90        mStrongAuthTrackers.add(tracker);
91
92        for (int i = 0; i < mStrongAuthForUser.size(); i++) {
93            int key = mStrongAuthForUser.keyAt(i);
94            int value = mStrongAuthForUser.valueAt(i);
95            try {
96                tracker.onStrongAuthRequiredChanged(value, key);
97            } catch (RemoteException e) {
98                Slog.e(TAG, "Exception while adding StrongAuthTracker.", e);
99            }
100        }
101    }
102
103    private void handleRemoveStrongAuthTracker(IStrongAuthTracker tracker) {
104        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
105            if (mStrongAuthTrackers.get(i).asBinder() == tracker.asBinder()) {
106                mStrongAuthTrackers.remove(i);
107                return;
108            }
109        }
110    }
111
112    private void handleRequireStrongAuth(int strongAuthReason, int userId) {
113        if (userId == UserHandle.USER_ALL) {
114            for (int i = 0; i < mStrongAuthForUser.size(); i++) {
115                int key = mStrongAuthForUser.keyAt(i);
116                handleRequireStrongAuthOneUser(strongAuthReason, key);
117            }
118        } else {
119            handleRequireStrongAuthOneUser(strongAuthReason, userId);
120        }
121    }
122
123    private void handleRequireStrongAuthOneUser(int strongAuthReason, int userId) {
124        int oldValue = mStrongAuthForUser.get(userId, mDefaultStrongAuthFlags);
125        int newValue = strongAuthReason == STRONG_AUTH_NOT_REQUIRED
126                ? STRONG_AUTH_NOT_REQUIRED
127                : (oldValue | strongAuthReason);
128        if (oldValue != newValue) {
129            mStrongAuthForUser.put(userId, newValue);
130            notifyStrongAuthTrackers(newValue, userId);
131        }
132    }
133
134    private void handleRemoveUser(int userId) {
135        int index = mStrongAuthForUser.indexOfKey(userId);
136        if (index >= 0) {
137            mStrongAuthForUser.removeAt(index);
138            notifyStrongAuthTrackers(mDefaultStrongAuthFlags, userId);
139        }
140    }
141
142    private void handleScheduleStrongAuthTimeout(int userId) {
143        final DevicePolicyManager dpm =
144                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
145        long when = SystemClock.elapsedRealtime() + dpm.getRequiredStrongAuthTimeout(null, userId);
146        // cancel current alarm listener for the user (if there was one)
147        StrongAuthTimeoutAlarmListener alarm = mStrongAuthTimeoutAlarmListenerForUser.get(userId);
148        if (alarm != null) {
149            mAlarmManager.cancel(alarm);
150        } else {
151            alarm = new StrongAuthTimeoutAlarmListener(userId);
152            mStrongAuthTimeoutAlarmListenerForUser.put(userId, alarm);
153        }
154        // schedule a new alarm listener for the user
155        mAlarmManager.set(AlarmManager.ELAPSED_REALTIME, when, STRONG_AUTH_TIMEOUT_ALARM_TAG,
156                alarm, mHandler);
157    }
158
159    private void notifyStrongAuthTrackers(int strongAuthReason, int userId) {
160        for (int i = 0; i < mStrongAuthTrackers.size(); i++) {
161            try {
162                mStrongAuthTrackers.get(i).onStrongAuthRequiredChanged(strongAuthReason, userId);
163            } catch (DeadObjectException e) {
164                Slog.d(TAG, "Removing dead StrongAuthTracker.");
165                mStrongAuthTrackers.remove(i);
166                i--;
167            } catch (RemoteException e) {
168                Slog.e(TAG, "Exception while notifying StrongAuthTracker.", e);
169            }
170        }
171    }
172
173    public void registerStrongAuthTracker(IStrongAuthTracker tracker) {
174        mHandler.obtainMessage(MSG_REGISTER_TRACKER, tracker).sendToTarget();
175    }
176
177    public void unregisterStrongAuthTracker(IStrongAuthTracker tracker) {
178        mHandler.obtainMessage(MSG_UNREGISTER_TRACKER, tracker).sendToTarget();
179    }
180
181    public void removeUser(int userId) {
182        final int argNotUsed = 0;
183        mHandler.obtainMessage(MSG_REMOVE_USER, userId, argNotUsed).sendToTarget();
184    }
185
186    public void requireStrongAuth(int strongAuthReason, int userId) {
187        if (userId == UserHandle.USER_ALL || userId >= UserHandle.USER_SYSTEM) {
188            mHandler.obtainMessage(MSG_REQUIRE_STRONG_AUTH, strongAuthReason,
189                    userId).sendToTarget();
190        } else {
191            throw new IllegalArgumentException(
192                    "userId must be an explicit user id or USER_ALL");
193        }
194    }
195
196    public void reportUnlock(int userId) {
197        requireStrongAuth(STRONG_AUTH_NOT_REQUIRED, userId);
198    }
199
200    public void reportSuccessfulStrongAuthUnlock(int userId) {
201        if (mFingerprintManager != null) {
202            byte[] token = null; /* TODO: pass real auth token once fp HAL supports it */
203            mFingerprintManager.resetTimeout(token);
204        }
205
206        final int argNotUsed = 0;
207        mHandler.obtainMessage(MSG_SCHEDULE_STRONG_AUTH_TIMEOUT, userId, argNotUsed).sendToTarget();
208    }
209
210    private class StrongAuthTimeoutAlarmListener implements OnAlarmListener {
211
212        private final int mUserId;
213
214        public StrongAuthTimeoutAlarmListener(int userId) {
215            mUserId = userId;
216        }
217
218        @Override
219        public void onAlarm() {
220            requireStrongAuth(STRONG_AUTH_REQUIRED_AFTER_TIMEOUT, mUserId);
221        }
222    }
223
224    private final Handler mHandler = new Handler() {
225        @Override
226        public void handleMessage(Message msg) {
227            switch (msg.what) {
228                case MSG_REGISTER_TRACKER:
229                    handleAddStrongAuthTracker((IStrongAuthTracker) msg.obj);
230                    break;
231                case MSG_UNREGISTER_TRACKER:
232                    handleRemoveStrongAuthTracker((IStrongAuthTracker) msg.obj);
233                    break;
234                case MSG_REQUIRE_STRONG_AUTH:
235                    handleRequireStrongAuth(msg.arg1, msg.arg2);
236                    break;
237                case MSG_REMOVE_USER:
238                    handleRemoveUser(msg.arg1);
239                    break;
240                case MSG_SCHEDULE_STRONG_AUTH_TIMEOUT:
241                    handleScheduleStrongAuthTimeout(msg.arg1);
242                    break;
243            }
244        }
245    };
246}
247