• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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