1 /* 2 * Copyright (C) 2024 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.nfc; 18 19 import static android.content.Context.MODE_PRIVATE; 20 21 import android.app.ActivityManager; 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.SharedPreferences; 27 import android.nfc.NfcAdapter; 28 import android.os.Handler; 29 import android.os.HandlerThread; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.util.Log; 33 34 import org.json.JSONException; 35 import org.json.JSONObject; 36 37 import java.util.ArrayList; 38 import java.util.Iterator; 39 import java.util.List; 40 import java.util.concurrent.CountDownLatch; 41 import java.util.concurrent.TimeUnit; 42 import java.util.concurrent.atomic.AtomicInteger; 43 44 /** 45 * Used to migrate shared preferences files stored by 46 * {@link com.android.nfc.NfcService} from AOSP stack to NFC mainline 47 * module. 48 */ 49 public class SharedPreferencesMigration { 50 private static final String TAG = "SharedPreferencesMigration"; 51 private static final String PREF = "NfcServicePrefs"; 52 public static final String PREF_TAG_APP_LIST = "TagIntentAppPreferenceListPrefs"; 53 private static final String PREF_NFC_ON = "nfc_on"; 54 private static final String PREF_SECURE_NFC_ON = "secure_nfc_on"; 55 private static final String PREF_NFC_READER_OPTION_ON = "nfc_reader_on"; 56 private static final String PREF_MIGRATION_TO_MAINLINE_COMPLETE = "migration_to_mainline_complete"; 57 58 private final SharedPreferences mSharedPreferences; 59 private SharedPreferences mTagAppPrefListPreferences; 60 private final Context mContext; 61 private final NfcAdapter mNfcAdapter; 62 SharedPreferencesMigration(Context context)63 public SharedPreferencesMigration(Context context) { 64 mContext = context; 65 mNfcAdapter = NfcAdapter.getDefaultAdapter(context); 66 if (mNfcAdapter == null) { 67 throw new IllegalStateException("Failed to get NFC adapter"); 68 } 69 SharedPreferences sharedPreferences = context.getSharedPreferences(PREF, MODE_PRIVATE); 70 SharedPreferences tagAppPrefListPreferences = 71 context.getSharedPreferences(PREF_TAG_APP_LIST, MODE_PRIVATE); 72 // Check both CE & DE directory for migration. 73 if (sharedPreferences.getAll().isEmpty() && tagAppPrefListPreferences.getAll().isEmpty()) { 74 Log.d(TAG, "Searching for NFC preferences in CE directory"); 75 Context ceContext = context.createCredentialProtectedStorageContext(); 76 sharedPreferences = ceContext.getSharedPreferences(PREF, MODE_PRIVATE); 77 tagAppPrefListPreferences = 78 ceContext.getSharedPreferences(PREF_TAG_APP_LIST, MODE_PRIVATE); 79 } 80 mSharedPreferences = sharedPreferences; 81 mTagAppPrefListPreferences = tagAppPrefListPreferences; 82 } 83 hasAlreadyMigrated()84 public boolean hasAlreadyMigrated() { 85 return mSharedPreferences.getAll().isEmpty() || 86 mSharedPreferences.getBoolean(PREF_MIGRATION_TO_MAINLINE_COMPLETE, false); 87 } 88 markMigrationComplete()89 public void markMigrationComplete() { 90 mSharedPreferences.edit().putBoolean(PREF_MIGRATION_TO_MAINLINE_COMPLETE, true).apply(); 91 } 92 getEnabledUserIds()93 private List<Integer> getEnabledUserIds() { 94 List<Integer> userIds = new ArrayList<Integer>(); 95 UserManager um = 96 mContext.createContextAsUser(UserHandle.of(ActivityManager.getCurrentUser()), 0) 97 .getSystemService(UserManager.class); 98 List<UserHandle> luh = um.getEnabledProfiles(); 99 for (UserHandle uh : luh) { 100 userIds.add(uh.getIdentifier()); 101 } 102 return userIds; 103 } 104 setNfcEnabled(boolean enable)105 private boolean setNfcEnabled(boolean enable) { 106 try { 107 if (mNfcAdapter.isEnabled() == enable) return true; 108 CountDownLatch countDownLatch = new CountDownLatch(1); 109 AtomicInteger state = new AtomicInteger(NfcAdapter.STATE_OFF); 110 BroadcastReceiver nfcChangeListener = new BroadcastReceiver() { 111 @Override 112 public void onReceive(Context context, Intent intent) { 113 int s = intent.getIntExtra(NfcAdapter.EXTRA_ADAPTER_STATE, NfcAdapter.STATE_OFF); 114 if (s == NfcAdapter.STATE_TURNING_ON || s == NfcAdapter.STATE_TURNING_OFF) { 115 return; 116 } 117 context.unregisterReceiver(this); 118 state.set(s); 119 countDownLatch.countDown(); 120 } 121 }; 122 HandlerThread handlerThread = new HandlerThread("nfc_migration_state_listener"); 123 handlerThread.start(); 124 Handler handler = new Handler(handlerThread.getLooper()); 125 IntentFilter intentFilter = new IntentFilter(); 126 intentFilter.addAction(NfcAdapter.ACTION_ADAPTER_STATE_CHANGED); 127 mContext.getApplicationContext().registerReceiver( 128 nfcChangeListener, intentFilter, null, handler); 129 if (enable) { 130 if (!mNfcAdapter.enable()) return false; 131 } else { 132 if (!mNfcAdapter.disable()) return false; 133 } 134 if (!countDownLatch.await(2000, TimeUnit.MILLISECONDS)) return false; 135 return state.get() == (enable ? NfcAdapter.STATE_ON : NfcAdapter.STATE_OFF); 136 } catch (Exception e) { 137 e.printStackTrace(); 138 return false; 139 } 140 } 141 handleMigration()142 public void handleMigration() { 143 Log.i(TAG, "handleMigration: Migrating preferences: " + mSharedPreferences.getAll() 144 + ", " + mTagAppPrefListPreferences.getAll()); 145 if (mSharedPreferences.contains(PREF_NFC_ON)) { 146 boolean enableNfc = mSharedPreferences.getBoolean(PREF_NFC_ON, false); 147 Log.d(TAG, "handleMigration: enableNfc: " + enableNfc); 148 if (!setNfcEnabled(enableNfc)) { 149 Log.e(TAG, "handleMigration: Failed to set NFC " 150 + (enableNfc ? "enabled" : "disabled")); 151 } 152 } 153 if (mSharedPreferences.contains(PREF_SECURE_NFC_ON)) { 154 boolean enableSecureNfc = mSharedPreferences.getBoolean(PREF_SECURE_NFC_ON, false); 155 Log.d(TAG, "handleMigration: enableSecureNfc: " + enableSecureNfc); 156 if (!mNfcAdapter.enableSecureNfc(enableSecureNfc)) { 157 Log.e(TAG, "handleMigration: enableSecureNfc failed"); 158 } 159 } 160 if (mSharedPreferences.contains(PREF_NFC_READER_OPTION_ON)) { 161 boolean enableReaderOption = 162 mSharedPreferences.getBoolean(PREF_NFC_READER_OPTION_ON, false); 163 Log.d(TAG, "handleMigration: enableSecureNfc: " + enableReaderOption); 164 if (!mNfcAdapter.enableReaderOption(enableReaderOption)) { 165 Log.e(TAG, "handleMigration: enableReaderOption failed"); 166 } 167 } 168 if (mTagAppPrefListPreferences != null) { 169 try { 170 for (Integer userId : getEnabledUserIds()) { 171 String jsonString = 172 mTagAppPrefListPreferences.getString(Integer.toString(userId), 173 (new JSONObject()).toString()); 174 if (jsonString != null) { 175 JSONObject jsonObject = new JSONObject(jsonString); 176 Iterator<String> keysItr = jsonObject.keys(); 177 while (keysItr.hasNext()) { 178 String pkg = keysItr.next(); 179 Boolean allow = jsonObject.getBoolean(pkg); 180 Log.d(TAG, "handleMigration: setTagIntentAppPreferenceForUser: " + pkg 181 + " = " + allow); 182 if (mNfcAdapter.setTagIntentAppPreferenceForUser(userId, pkg, 183 allow) != NfcAdapter.TAG_INTENT_APP_PREF_RESULT_SUCCESS) { 184 Log.e(TAG, 185 "handleMigration: setTagIntentAppPreferenceForUser failed"); 186 } 187 } 188 } 189 } 190 } catch (JSONException e) { 191 Log.e(TAG, "handleMigration: JSONException: " + e); 192 } 193 } 194 } 195 196 } 197