1 /*
2  * Copyright (C) 2015 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.tv.settings.device.storage;
18 
19 import android.annotation.Nullable;
20 import android.app.ActivityManager;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.os.AsyncTask;
24 import android.os.Bundle;
25 import android.os.UserHandle;
26 import android.service.oemlock.OemLockManager;
27 import android.service.persistentdata.PersistentDataBlockManager;
28 import android.util.Log;
29 
30 import androidx.annotation.NonNull;
31 import androidx.fragment.app.FragmentActivity;
32 import androidx.leanback.app.GuidedStepSupportFragment;
33 import androidx.leanback.widget.GuidanceStylist;
34 import androidx.leanback.widget.GuidedAction;
35 
36 import com.android.tv.settings.R;
37 import com.android.tv.settings.util.GuidedActionsAlignUtil;
38 
39 import java.util.List;
40 
41 public class ResetActivity extends FragmentActivity {
42 
43     private static final String TAG = "ResetActivity";
44 
45     /**
46      * Support for shutdown-after-reset. If our launch intent has a true value for
47      * the boolean extra under the following key, then include it in the intent we
48      * use to trigger a factory reset. This will cause us to shut down instead of
49      * restart after the reset.
50      */
51     private static final String SHUTDOWN_INTENT_EXTRA = "shutdown";
52 
53     @Override
onCreate(@ullable Bundle savedInstanceState)54     protected void onCreate(@Nullable Bundle savedInstanceState) {
55         super.onCreate(savedInstanceState);
56         if (savedInstanceState == null) {
57             GuidedStepSupportFragment
58                     .addAsRoot(this, ResetFragment.newInstance(), android.R.id.content);
59         }
60     }
61 
62     public static class ResetFragment extends GuidedStepSupportFragment {
63 
newInstance()64         public static ResetFragment newInstance() {
65 
66             Bundle args = new Bundle();
67 
68             ResetFragment fragment = new ResetFragment();
69             fragment.setArguments(args);
70             return fragment;
71         }
72 
73         @NonNull
74         @Override
onCreateGuidance(Bundle savedInstanceState)75         public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
76             return new GuidanceStylist.Guidance(
77                     getString(R.string.device_reset),
78                     getString(R.string.factory_reset_description),
79                     null,
80                     getContext().getDrawable(R.drawable.ic_settings_backup_restore_132dp));
81         }
82 
83         @Override
onCreateActions(@onNull List<GuidedAction> actions, Bundle savedInstanceState)84         public void onCreateActions(@NonNull List<GuidedAction> actions,
85                 Bundle savedInstanceState) {
86             actions.add(new GuidedAction.Builder(getContext())
87                     .clickAction(GuidedAction.ACTION_ID_CANCEL)
88                     .build());
89             actions.add(new GuidedAction.Builder(getContext())
90                     .clickAction(GuidedAction.ACTION_ID_OK)
91                     .title(getString(R.string.device_reset))
92                     .build());
93         }
94 
95         @Override
onGuidedActionClicked(GuidedAction action)96         public void onGuidedActionClicked(GuidedAction action) {
97             if (action.getId() == GuidedAction.ACTION_ID_OK) {
98                 add(getFragmentManager(), ResetConfirmFragment.newInstance());
99             } else if (action.getId() == GuidedAction.ACTION_ID_CANCEL) {
100                 getActivity().finish();
101             } else {
102                 Log.wtf(TAG, "Unknown action clicked");
103             }
104         }
105 
106         @Override
onCreateGuidanceStylist()107         public GuidanceStylist onCreateGuidanceStylist() {
108             return GuidedActionsAlignUtil.createGuidanceStylist();
109         }
110     }
111 
112     public static class ResetConfirmFragment extends GuidedStepSupportFragment {
113 
newInstance()114         public static ResetConfirmFragment newInstance() {
115 
116             Bundle args = new Bundle();
117 
118             ResetConfirmFragment fragment = new ResetConfirmFragment();
119             fragment.setArguments(args);
120             return fragment;
121         }
122 
123         @NonNull
124         @Override
onCreateGuidance(Bundle savedInstanceState)125         public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
126             return new GuidanceStylist.Guidance(
127                     getString(R.string.device_reset),
128                     getString(R.string.confirm_factory_reset_description),
129                     null,
130                     getContext().getDrawable(R.drawable.ic_settings_backup_restore_132dp));
131         }
132 
133         @Override
onCreateActions(@onNull List<GuidedAction> actions, Bundle savedInstanceState)134         public void onCreateActions(@NonNull List<GuidedAction> actions,
135                 Bundle savedInstanceState) {
136             actions.add(new GuidedAction.Builder(getContext())
137                     .clickAction(GuidedAction.ACTION_ID_CANCEL)
138                     .build());
139             actions.add(new GuidedAction.Builder(getContext())
140                     .clickAction(GuidedAction.ACTION_ID_OK)
141                     .title(getString(R.string.confirm_factory_reset_device))
142                     .build());
143         }
144 
145         @Override
onCreateGuidanceStylist()146         public GuidanceStylist onCreateGuidanceStylist() {
147             return GuidedActionsAlignUtil.createGuidanceStylist();
148         }
149 
150         @Override
onGuidedActionClicked(GuidedAction action)151         public void onGuidedActionClicked(GuidedAction action) {
152             if (action.getId() == GuidedAction.ACTION_ID_OK) {
153                 if (ActivityManager.isUserAMonkey()) {
154                     Log.v(TAG, "Monkey tried to erase the device. Bad monkey, bad!");
155                     getActivity().finish();
156                 } else {
157                     performFactoryReset();
158                 }
159             } else if (action.getId() == GuidedAction.ACTION_ID_CANCEL) {
160                 getActivity().finish();
161             } else {
162                 Log.wtf(TAG, "Unknown action clicked");
163             }
164         }
165 
performFactoryReset()166         private void performFactoryReset() {
167             final PersistentDataBlockManager pdbManager = (PersistentDataBlockManager)
168                     getContext().getSystemService(Context.PERSISTENT_DATA_BLOCK_SERVICE);
169 
170             if (shouldWipePersistentDataBlock(pdbManager)) {
171                 new AsyncTask<Void, Void, Void>() {
172                     @Override
173                     protected Void doInBackground(Void... params) {
174                         pdbManager.wipe();
175                         return null;
176                     }
177 
178                     @Override
179                     protected void onPostExecute(Void aVoid) {
180                         doMainClear();
181                     }
182                 }.execute();
183             } else {
184                 doMainClear();
185             }
186         }
187 
shouldWipePersistentDataBlock(PersistentDataBlockManager pdbManager)188         private boolean shouldWipePersistentDataBlock(PersistentDataBlockManager pdbManager) {
189             if (pdbManager == null) {
190                 return false;
191             }
192             // If OEM unlock is allowed, the persistent data block will be wiped during FR.
193             // If disabled, it will be wiped here instead.
194             if (((OemLockManager) getActivity().getSystemService(Context.OEM_LOCK_SERVICE))
195                     .isOemUnlockAllowed()) {
196                 return false;
197             }
198             return true;
199         }
200 
doMainClear()201         private void doMainClear() {
202             if (getActivity() == null) {
203                 return;
204             }
205             Intent resetIntent = new Intent(Intent.ACTION_FACTORY_RESET);
206             resetIntent.setPackage("android");
207             resetIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
208             resetIntent.putExtra(Intent.EXTRA_REASON, "ResetConfirmFragment");
209             if (getActivity().getIntent().getBooleanExtra(SHUTDOWN_INTENT_EXTRA, false)) {
210                 resetIntent.putExtra(SHUTDOWN_INTENT_EXTRA, true);
211             }
212             getActivity().sendBroadcastAsUser(resetIntent, UserHandle.SYSTEM);
213         }
214     }
215 }
216