WorkLockActivity.java revision 5d59242c2ec590c5bc57744d4a7ab62890cc76c8
1/*
2 * Copyright (C) 2017 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.systemui.keyguard;
18
19import static android.app.ActivityManager.TaskDescription;
20import static android.app.ActivityManager.StackId;
21
22import android.annotation.ColorInt;
23import android.annotation.UserIdInt;
24import android.app.Activity;
25import android.app.ActivityManager;
26import android.app.ActivityOptions;
27import android.app.KeyguardManager;
28import android.app.PendingIntent;
29import android.app.admin.DevicePolicyManager;
30import android.content.BroadcastReceiver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.graphics.Color;
35import android.graphics.Rect;
36import android.os.Bundle;
37import android.os.RemoteException;
38import android.os.UserHandle;
39import android.util.Log;
40import android.view.View;
41
42/**
43 * Bouncer between work activities and the activity used to confirm credentials before unlocking
44 * a managed profile.
45 * <p>
46 * Shows a solid color when started, based on the organization color of the user it is supposed to
47 * be blocking. Once focused, it switches to a screen to confirm credentials and auto-dismisses if
48 * credentials are accepted.
49 */
50public class WorkLockActivity extends Activity {
51    private static final String TAG = "WorkLockActivity";
52
53    /**
54     * ID of the locked user that this activity blocks access to.
55     */
56    @UserIdInt
57    private int mUserId;
58
59    /**
60     * {@see KeyguardManager}
61     */
62    private KeyguardManager mKgm;
63
64    /**
65     * {@see DevicePolicyManager}
66     */
67    private DevicePolicyManager mDpm;
68
69    @Override
70    public void onCreate(Bundle savedInstanceState) {
71        super.onCreate(savedInstanceState);
72
73        mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
74        mDpm = (DevicePolicyManager) getSystemService(Context.DEVICE_POLICY_SERVICE);
75        mKgm = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
76
77        final IntentFilter lockFilter = new IntentFilter();
78        lockFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
79        registerReceiverAsUser(mLockEventReceiver, UserHandle.ALL, lockFilter,
80                /* permission */ null, /* scheduler */ null);
81
82        // Once the receiver is registered, check whether anything happened between now and the time
83        // when this activity was launched. If it did and the user is unlocked now, just quit.
84        if (!mKgm.isDeviceLocked(mUserId)) {
85            finish();
86            return;
87        }
88
89        // Get the organization color; this is a 24-bit integer provided by a DPC, guaranteed to
90        // be completely opaque.
91        final @ColorInt int color = mDpm.getOrganizationColorForUser(mUserId);
92
93        // Draw captions overlaid on the content view, so the whole window is one solid color.
94        setOverlayWithDecorCaptionEnabled(true);
95
96        // Blank out the activity. When it is on-screen it will look like a Recents thumbnail with
97        // redaction switched on.
98        final View blankView = new View(this);
99        blankView.setBackgroundColor(color);
100        setContentView(blankView);
101    }
102
103    /**
104     * Respond to focus events by showing the prompt to confirm credentials.
105     * <p>
106     * We don't have anything particularly interesting to show here (just a solid-colored page) so
107     * there is no sense in sitting in the foreground doing nothing.
108     */
109    @Override
110    public void onWindowFocusChanged(boolean hasFocus) {
111        if (hasFocus) {
112            showConfirmCredentialActivity();
113        }
114    }
115
116    @Override
117    public void onDestroy() {
118        unregisterReceiver(mLockEventReceiver);
119        super.onDestroy();
120    }
121
122    @Override
123    public void onBackPressed() {
124        // Ignore back presses.
125        return;
126    }
127
128    @Override
129    public void setTaskDescription(TaskDescription taskDescription) {
130        // Use the previous activity's task description.
131    }
132
133    private final BroadcastReceiver mLockEventReceiver = new BroadcastReceiver() {
134        @Override
135        public void onReceive(Context context, Intent intent) {
136            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, mUserId);
137            if (userId == mUserId && !mKgm.isDeviceLocked(mUserId)) {
138                finish();
139            }
140        }
141    };
142
143    private void showConfirmCredentialActivity() {
144        if (isFinishing() || !mKgm.isDeviceLocked(mUserId)) {
145            // Don't show the confirm credentials screen if we are already unlocked / unlocking.
146            return;
147        }
148
149        final Intent credential = mKgm.createConfirmDeviceCredentialIntent(null, null, mUserId);
150        if (credential == null) {
151            return;
152        }
153
154        final ActivityOptions options = ActivityOptions.makeBasic();
155        options.setLaunchTaskId(getTaskId());
156
157        // Bring this activity back to the foreground after confirming credentials.
158        final PendingIntent target = PendingIntent.getActivity(this, /* request */ -1, getIntent(),
159                PendingIntent.FLAG_CANCEL_CURRENT |
160                PendingIntent.FLAG_ONE_SHOT |
161                PendingIntent.FLAG_IMMUTABLE, options.toBundle());
162
163        credential.putExtra(Intent.EXTRA_INTENT, target.getIntentSender());
164        try {
165            ActivityManager.getService().startConfirmDeviceCredentialIntent(credential,
166                    getChallengeOptions().toBundle());
167        } catch (RemoteException e) {
168            Log.e(TAG, "Failed to start confirm credential intent", e);
169        }
170    }
171
172    private ActivityOptions getChallengeOptions() {
173        // If we are taking up the whole screen, just use the default animation of clipping the
174        // credentials activity into the entire foreground.
175        if (!isInMultiWindowMode()) {
176            return ActivityOptions.makeBasic();
177        }
178
179        // Otherwise, animate the transition from this part of the screen to fullscreen
180        // using our own decor as the starting position.
181        final View view = getWindow().getDecorView();
182        return ActivityOptions.makeScaleUpAnimation(view, 0, 0, view.getWidth(), view.getHeight());
183    }
184}
185