| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.car.admin; |
| |
| import static android.car.drivingstate.CarDrivingStateEvent.DRIVING_STATE_PARKED; |
| |
| import static com.android.car.admin.NotificationHelper.FACTORY_RESET_NOTIFICATION_ID; |
| |
| import android.app.Activity; |
| import android.app.ActivityManager; |
| import android.app.AlertDialog; |
| import android.app.Notification; |
| import android.app.NotificationManager; |
| import android.app.PendingIntent; |
| import android.car.Car; |
| import android.car.drivingstate.CarDrivingStateEvent; |
| import android.car.drivingstate.CarDrivingStateManager; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.os.Bundle; |
| import android.os.IBinder; |
| import android.os.UserHandle; |
| import android.util.Slog; |
| import android.widget.Button; |
| import android.widget.Toast; |
| |
| import com.android.car.CarLog; |
| import com.android.car.R; |
| import com.android.internal.os.IResultReceiver; |
| |
| // TODO(b/171603586): STOPSHIP move this class to CarSettings |
| /** |
| * Activity shown when a factory request is imminent, it gives the user the option to reset now or |
| * wait until the device is rebooted / resumed from suspend. |
| */ |
| public final class FactoryResetActivity extends Activity { |
| |
| private static final String TAG = CarLog.tagFor(FactoryResetActivity.class); |
| |
| public static final String EXTRA_CALLBACK = "factory_reset_callback"; |
| |
| private Button mNowButton; |
| private Button mLaterButton; |
| private IResultReceiver mCallback; |
| |
| private Car mCar; |
| private CarDrivingStateManager mCarDrivingStateManager; |
| /** |
| * Sends the notification warning the user about the factory reset. |
| */ |
| public static void sendNotification(Context context, IResultReceiver callback) { |
| // The factory request is received by CarService - which runs on system user - but the |
| // notification will be sent to all users. |
| UserHandle currentUser = UserHandle.of(ActivityManager.getCurrentUser()); |
| |
| @SuppressWarnings("deprecation") |
| Intent intent = new Intent(context, FactoryResetActivity.class) |
| .putExtra(EXTRA_CALLBACK, callback.asBinder()); |
| PendingIntent pendingIntent = PendingIntent.getActivityAsUser(context, |
| FACTORY_RESET_NOTIFICATION_ID, intent, PendingIntent.FLAG_IMMUTABLE, |
| /* options= */ null, currentUser); |
| |
| // TODO (b/13679261) allows OEM to customize the package name shown in notification |
| Notification notification = NotificationHelper |
| .newNotificationBuilder(context, NotificationManager.IMPORTANCE_HIGH) |
| .setSmallIcon(R.drawable.car_ic_warning) |
| .setColor(context.getColor(R.color.red_warning)) |
| .setContentTitle(context.getString(R.string.factory_reset_notification_title)) |
| .setContentText(context.getString(R.string.factory_reset_notification_text)) |
| .setCategory(Notification.CATEGORY_CAR_WARNING) |
| .setOngoing(true) |
| .addAction(/* icon= */ 0, |
| context.getString(R.string.factory_reset_notification_button), |
| pendingIntent) |
| .build(); |
| |
| Slog.i(TAG, "Showing factory reset notification on all users"); |
| context.getSystemService(NotificationManager.class) |
| .notifyAsUser(TAG, FACTORY_RESET_NOTIFICATION_ID, notification, UserHandle.ALL); |
| } |
| |
| @Override |
| protected void onCreate(Bundle savedInstanceState) { |
| super.onCreate(savedInstanceState); |
| |
| Intent intent = getIntent(); |
| Object binder = null; |
| |
| try { |
| binder = intent.getExtra(EXTRA_CALLBACK); |
| mCallback = IResultReceiver.Stub.asInterface((IBinder) binder); |
| } catch (Exception e) { |
| Slog.w(TAG, "error getting IResultReveiver from " + EXTRA_CALLBACK + " extra (" |
| + binder + ") on " + intent, e); |
| } |
| |
| if (mCallback == null) { |
| Slog.wtf(TAG, "no IResultReceiver / " + EXTRA_CALLBACK + " extra on " + intent); |
| finish(); |
| return; |
| } |
| |
| // Connect to car service |
| mCar = Car.createCar(this); |
| mCarDrivingStateManager = (CarDrivingStateManager) mCar.getCarManager( |
| Car.CAR_DRIVING_STATE_SERVICE); |
| showMore(); |
| } |
| |
| @Override |
| protected void onStop() { |
| super.onStop(); |
| finish(); |
| } |
| |
| private void showMore() { |
| CarDrivingStateEvent state = mCarDrivingStateManager.getCurrentCarDrivingState(); |
| switch (state.eventValue) { |
| case DRIVING_STATE_PARKED: |
| showFactoryResetDialog(); |
| break; |
| default: |
| showFactoryResetToast(); |
| } |
| } |
| |
| private void showFactoryResetDialog() { |
| // TODO(b/171603586): STOPSHIP use Chassis library after moving this class to CarSettings |
| AlertDialog dialog = new AlertDialog.Builder(/* context= */ this, |
| com.android.internal.R.style.Theme_DeviceDefault_Dialog_Alert) |
| .setTitle(R.string.factory_reset_parked_title) |
| .setMessage(R.string.factory_reset_parked_text) |
| .setPositiveButton(R.string.factory_reset_later_button, |
| (d, which) -> factoryResetLater()) |
| .setNegativeButton(R.string.factory_reset_now_button, |
| (d, which) -> factoryResetNow()) |
| .setCancelable(false) |
| .setOnDismissListener((d) -> finish()) |
| .create(); |
| |
| dialog.show(); |
| } |
| |
| private void showFactoryResetToast() { |
| showToast(R.string.factory_reset_driving_text); |
| finish(); |
| } |
| |
| private void factoryResetNow() { |
| Slog.i(TAG, "Factory reset acknowledged; finishing it"); |
| |
| try { |
| mCallback.send(/* resultCode= */ 0, /* resultData= */ null); |
| |
| // Cancel pending intent and notification |
| getSystemService(NotificationManager.class).cancel(FACTORY_RESET_NOTIFICATION_ID); |
| PendingIntent.getActivity(this, FACTORY_RESET_NOTIFICATION_ID, getIntent(), |
| PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT).cancel(); |
| } catch (Exception e) { |
| Slog.e(TAG, "error factory resetting or cancelling notification / intent", e); |
| return; |
| } finally { |
| finish(); |
| } |
| } |
| |
| private void factoryResetLater() { |
| Slog.i(TAG, "Delaying factory reset."); |
| showToast(R.string.factory_reset_later_text); |
| finish(); |
| } |
| |
| private void showToast(int resId) { |
| Toast.makeText(this, resId, Toast.LENGTH_LONG).show(); |
| } |
| } |