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