• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 
17 package com.android.car.admin;
18 
19 import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED;
20 
21 import static com.android.car.admin.NotificationHelper.FACTORY_RESET_NOTIFICATION_ID;
22 
23 import android.app.Activity;
24 import android.app.ActivityManager;
25 import android.app.AlertDialog;
26 import android.app.Notification;
27 import android.app.NotificationManager;
28 import android.app.PendingIntent;
29 import android.car.Car;
30 import android.car.drivingstate.CarDrivingStateEvent;
31 import android.car.drivingstate.CarDrivingStateManager;
32 import android.content.Context;
33 import android.content.Intent;
34 import android.os.Bundle;
35 import android.os.IBinder;
36 import android.os.UserHandle;
37 import android.util.Slog;
38 import android.widget.Button;
39 import android.widget.Toast;
40 
41 import com.android.car.CarLog;
42 import com.android.car.R;
43 import com.android.internal.os.IResultReceiver;
44 
45 // TODO(b/171603586): STOPSHIP move this class to CarSettings
46 /**
47  * Activity shown when a factory request is imminent, it gives the user the option to reset now or
48  * wait until the device is rebooted / resumed from suspend.
49  */
50 public final class FactoryResetActivity extends Activity {
51 
52     private static final String TAG = CarLog.tagFor(FactoryResetActivity.class);
53 
54     public static final String EXTRA_CALLBACK = "factory_reset_callback";
55 
56     private Button mNowButton;
57     private Button mLaterButton;
58     private IResultReceiver mCallback;
59 
60     private Car mCar;
61     private CarDrivingStateManager mCarDrivingStateManager;
62     /**
63      * Sends the notification warning the user about the factory reset.
64      */
sendNotification(Context context, IResultReceiver callback)65     public static void sendNotification(Context context, IResultReceiver callback) {
66         // The factory request is received by CarService - which runs on system user - but the
67         // notification will be sent to all users.
68         UserHandle currentUser = UserHandle.of(ActivityManager.getCurrentUser());
69 
70         @SuppressWarnings("deprecation")
71         Intent intent = new Intent(context, FactoryResetActivity.class)
72                 .putExtra(EXTRA_CALLBACK, callback.asBinder());
73         PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context,
74                 FACTORY_RESET_NOTIFICATION_ID, intent, PendingIntent.FLAG_IMMUTABLE,
75                 /* options= */ null, currentUser);
76 
77         // TODO (b/13679261) allows OEM to customize the package name shown in notification
78         Notification notification = NotificationHelper
79                 .newNotificationBuilder(context, NotificationManager.IMPORTANCE_HIGH)
80                 .setSmallIcon(R.drawable.car_ic_warning)
81                 .setColor(context.getColor(R.color.red_warning))
82                 .setContentTitle(context.getString(R.string.factory_reset_notification_title))
83                 .setContentText(context.getString(R.string.factory_reset_notification_text))
84                 .setCategory(Notification.CATEGORY_CAR_WARNING)
85                 .setOngoing(true)
86                 .addAction(/* icon= */ 0,
87                         context.getString(R.string.factory_reset_notification_button),
88                         pendingIntent)
89                 .build();
90 
91         Slog.i(TAG, "Showing factory reset notification on all users");
92         context.getSystemService(NotificationManager.class)
93                 .notifyAsUser(TAG, FACTORY_RESET_NOTIFICATION_ID, notification, UserHandle.ALL);
94     }
95 
96     @Override
onCreate(Bundle savedInstanceState)97     protected void onCreate(Bundle savedInstanceState) {
98         super.onCreate(savedInstanceState);
99 
100         Intent intent = getIntent();
101         Object binder = null;
102 
103         try {
104             binder = intent.getExtra(EXTRA_CALLBACK);
105             mCallback = IResultReceiver.Stub.asInterface((IBinder) binder);
106         } catch (Exception e) {
107             Slog.w(TAG, "error getting IResultReveiver from " + EXTRA_CALLBACK + " extra ("
108                     + binder + ") on " + intent, e);
109         }
110 
111         if (mCallback == null) {
112             Slog.wtf(TAG, "no IResultReceiver / " + EXTRA_CALLBACK + " extra  on " + intent);
113             finish();
114             return;
115         }
116 
117         // Connect to car service
118         mCar = Car.createCar(this);
119         mCarDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager(
120                 Car.CAR_DRIVING_STATE_SERVICE);
121         showMore();
122     }
123 
124     @Override
onStop()125     protected void onStop() {
126         super.onStop();
127         finish();
128     }
129 
showMore()130     private void showMore() {
131         CarDrivingStateEvent state = mCarDrivingStateManager.getCurrentCarDrivingState();
132         switch (state.eventValue) {
133             case DRIVING_STATE_PARKED:
134                 showFactoryResetDialog();
135                 break;
136             default:
137                 showFactoryResetToast();
138         }
139     }
140 
showFactoryResetDialog()141     private void showFactoryResetDialog() {
142         // TODO(b/171603586): STOPSHIP use Chassis library after moving this class to CarSettings
143         AlertDialog dialog = new AlertDialog.Builder(/* context= */ this,
144                         com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert)
145                 .setTitle(R.string.factory_reset_parked_title)
146                 .setMessage(R.string.factory_reset_parked_text)
147                 .setPositiveButton(R.string.factory_reset_later_button,
148                         (d, which) -> factoryResetLater())
149                 .setNegativeButton(R.string.factory_reset_now_button,
150                         (d, which) -> factoryResetNow())
151                 .setCancelable(false)
152                 .setOnDismissListener((d) -> finish())
153                 .create();
154 
155         dialog.show();
156     }
157 
showFactoryResetToast()158     private void showFactoryResetToast() {
159         showToast(R.string.factory_reset_driving_text);
160         finish();
161     }
162 
factoryResetNow()163     private void factoryResetNow() {
164         Slog.i(TAG, "Factory reset acknowledged; finishing it");
165 
166         try {
167             mCallback.send(/* resultCode= */ 0, /* resultData= */ null);
168 
169             // Cancel pending intent and notification
170             getSystemService(NotificationManager.class).cancel(FACTORY_RESET_NOTIFICATION_ID);
171             PendingIntent.getActivity(this, FACTORY_RESET_NOTIFICATION_ID, getIntent(),
172                     PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT).cancel();
173         } catch (Exception e) {
174             Slog.e(TAG, "error factory resetting or cancelling notification / intent", e);
175             return;
176         } finally {
177             finish();
178         }
179     }
180 
factoryResetLater()181     private void factoryResetLater() {
182         Slog.i(TAG, "Delaying factory reset.");
183         showToast(R.string.factory_reset_later_text);
184         finish();
185     }
186 
showToast(int resId)187     private void showToast(int resId) {
188         Toast.makeText(this, resId, Toast.LENGTH_LONG).show();
189     }
190 }
191