blob: 58dd343df30a66f9186c37dae4ccea8a6f6a0081 [file] [log] [blame]
/*
* 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();
}
}