• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package com.android.car.pm;
17 
18 import android.app.Activity;
19 import android.car.Car;
20 import android.car.CarNotConnectedException;
21 import android.car.content.pm.CarPackageManager;
22 import android.car.drivingstate.CarUxRestrictions;
23 import android.car.drivingstate.CarUxRestrictionsManager;
24 import android.content.ComponentName;
25 import android.content.Intent;
26 import android.content.ServiceConnection;
27 import android.content.pm.ApplicationInfo;
28 import android.content.pm.PackageManager;
29 import android.os.Bundle;
30 import android.os.IBinder;
31 import android.text.TextUtils;
32 import android.util.Log;
33 import android.view.View;
34 import android.widget.Button;
35 import android.widget.TextView;
36 
37 import com.android.car.CarLog;
38 import com.android.car.R;
39 
40 /**
41  * Default activity that will be launched when the current foreground activity is not allowed.
42  * Additional information on blocked Activity will be passed as extra in Intent
43  * via {@link #INTENT_KEY_BLOCKED_ACTIVITY} key.
44  */
45 public class ActivityBlockingActivity extends Activity {
46     public static final String INTENT_KEY_BLOCKED_ACTIVITY = "blocked_activity";
47     public static final String EXTRA_BLOCKED_TASK = "blocked_task";
48 
49     private static final int INVALID_TASK_ID = -1;
50 
51     private Car mCar;
52     private CarUxRestrictionsManager mUxRManager;
53 
54     private TextView mBlockedTitle;
55     private Button mExitButton;
56     // Exiting depends on Car connection, which might not be available at the time exit was
57     // requested (e.g. user presses Exit Button). In that case, we record exiting was requested, and
58     // Car connection will perform exiting once it is established.
59     private boolean mExitRequested;
60     private int mBlockedTaskId;
61 
62     @Override
onCreate(Bundle savedInstanceState)63     protected void onCreate(Bundle savedInstanceState) {
64         super.onCreate(savedInstanceState);
65         setContentView(R.layout.activity_blocking);
66 
67         mBlockedTitle = findViewById(R.id.activity_blocked_title);
68         mExitButton = findViewById(R.id.exit);
69         mExitButton.setOnClickListener(v -> handleFinish());
70 
71         // Listen to the CarUxRestrictions so this blocking activity can be dismissed when the
72         // restrictions are lifted.
73         mCar = Car.createCar(this, new ServiceConnection() {
74             @Override
75             public void onServiceConnected(ComponentName name, IBinder service) {
76                 try {
77                     if (mExitRequested) {
78                         handleFinish();
79                     }
80                     mUxRManager = (CarUxRestrictionsManager) mCar.getCarManager(
81                             Car.CAR_UX_RESTRICTION_SERVICE);
82                     // This activity would have been launched only in a restricted state.
83                     // But ensuring when the service connection is established, that we are still
84                     // in a restricted state.
85                     handleUxRChange(mUxRManager.getCurrentCarUxRestrictions());
86                     mUxRManager.registerListener(ActivityBlockingActivity.this::handleUxRChange);
87                 } catch (CarNotConnectedException e) {
88                     Log.e(CarLog.TAG_AM, "Failed to get CarUxRestrictionsManager", e);
89                 }
90             }
91 
92             @Override
93             public void onServiceDisconnected(ComponentName name) {
94                 finish();
95                 mUxRManager = null;
96             }
97         });
98         mCar.connect();
99     }
100 
101     @Override
onResume()102     protected void onResume() {
103         super.onResume();
104 
105         // Display message about the current blocked activity, and optionally show an exit button
106         // to restart the blocked task (stack of activities) if its root activity is DO.
107 
108         // blockedActivity is expected to be always passed in as the topmost activity of task.
109         String blockedActivity = getIntent().getStringExtra(INTENT_KEY_BLOCKED_ACTIVITY);
110         mBlockedTitle.setText(getString(R.string.activity_blocked_string,
111                 findHumanReadableLabel(blockedActivity)));
112         if (Log.isLoggable(CarLog.TAG_AM, Log.DEBUG)) {
113             Log.d(CarLog.TAG_AM, "Blocking activity " + blockedActivity);
114         }
115 
116         // taskId is available as extra if the task can be restarted.
117         mBlockedTaskId = getIntent().getIntExtra(EXTRA_BLOCKED_TASK, INVALID_TASK_ID);
118 
119         mExitButton.setVisibility(mBlockedTaskId == INVALID_TASK_ID ? View.GONE : View.VISIBLE);
120         if (Log.isLoggable(CarLog.TAG_AM, Log.DEBUG) && mBlockedTaskId == INVALID_TASK_ID) {
121             Log.d(CarLog.TAG_AM, "Blocked task ID is not available. Hiding exit button.");
122         }
123     }
124 
125     @Override
onNewIntent(Intent intent)126     protected void onNewIntent(Intent intent) {
127         super.onNewIntent(intent);
128         setIntent(intent);
129     }
130 
131     @Override
onDestroy()132     protected void onDestroy() {
133         super.onDestroy();
134         if (mCar.isConnected() && mUxRManager != null) {
135             try {
136                 mUxRManager.unregisterListener();
137             } catch (CarNotConnectedException e) {
138                 Log.e(CarLog.TAG_AM, "Cannot unregisterListener", e);
139             }
140             mUxRManager = null;
141             mCar.disconnect();
142         }
143     }
144 
145     // If no distraction optimization is required in the new restrictions, then dismiss the
146     // blocking activity (self).
handleUxRChange(CarUxRestrictions restrictions)147     private void handleUxRChange(CarUxRestrictions restrictions) {
148         if (restrictions == null) {
149             return;
150         }
151         if (!restrictions.isRequiresDistractionOptimization()) {
152             finish();
153         }
154     }
155 
156     /**
157      * Returns a human-readable string for {@code flattenComponentName}.
158      *
159      * <p>It first attempts to return the application label for this activity. If that fails,
160      * it will return the last part in the activity name.
161      */
findHumanReadableLabel(String flattenComponentName)162     private String findHumanReadableLabel(String flattenComponentName) {
163         ComponentName componentName = ComponentName.unflattenFromString(flattenComponentName);
164         String label = null;
165         // Attempt to find application label.
166         try {
167             ApplicationInfo applicationInfo = getPackageManager().getApplicationInfo(
168                     componentName.getPackageName(), 0);
169             CharSequence appLabel = getPackageManager().getApplicationLabel(applicationInfo);
170             if (appLabel != null) {
171                 label = appLabel.toString();
172             }
173         } catch (PackageManager.NameNotFoundException e) {
174             if (Log.isLoggable(CarLog.TAG_AM, Log.INFO)) {
175                 Log.i(CarLog.TAG_AM, "Could not find package for component name "
176                         + componentName.toString());
177             }
178         }
179         if (TextUtils.isEmpty(label)) {
180             label = componentName.getClass().getSimpleName();
181         }
182         return label;
183     }
184 
handleFinish()185     private void handleFinish() {
186         if (!mCar.isConnected()) {
187             mExitRequested = true;
188             return;
189         }
190         if (isFinishing()) {
191             return;
192         }
193 
194         // Lock on self (assuming single instance) to avoid restarting the same task twice.
195         synchronized (this) {
196             try {
197                 CarPackageManager carPm = (CarPackageManager)
198                         mCar.getCarManager(Car.PACKAGE_SERVICE);
199                 carPm.restartTask(mBlockedTaskId);
200             } catch (CarNotConnectedException e) {
201                 // We should never be here since Car connection is already checked.
202                 Log.e(CarLog.TAG_AM, "Car connection is not available.", e);
203                 return;
204             }
205             finish();
206         }
207     }
208 }
209