126515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen/* 226515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * Copyright (C) 2013 The Android Open Source Project 326515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * 426515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * Licensed under the Apache License, Version 2.0 (the "License"); 526515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * you may not use this file except in compliance with the License. 626515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * You may obtain a copy of the License at 726515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * 826515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * http://www.apache.org/licenses/LICENSE-2.0 926515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * 1026515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * Unless required by applicable law or agreed to in writing, software 1126515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * distributed under the License is distributed on an "AS IS" BASIS, 1226515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1326515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * See the License for the specific language governing permissions and 1426515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen * limitations under the License. 1526515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen */ 1626515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 1726515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenenpackage com.android.settings.nfc; 1826515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 19fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenenimport android.app.Activity; 206d5cac594620bd270f263fedfb8a23d6d916b014Martijn Coenenimport android.content.ComponentName; 216d5cac594620bd270f263fedfb8a23d6d916b014Martijn Coenenimport android.content.Context; 2226515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenenimport android.content.pm.PackageManager; 23fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenenimport android.content.res.Resources; 2426515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenenimport android.graphics.drawable.Drawable; 2526515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenenimport android.nfc.NfcAdapter; 2626515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenenimport android.nfc.cardemulation.ApduServiceInfo; 27a73aa32278adb177042d4b5c873a0f08deb1d3f3Martijn Coenenimport android.nfc.cardemulation.CardEmulation; 28fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenenimport android.os.Bundle; 29fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenenimport android.os.Handler; 30fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenenimport android.os.Message; 31fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenenimport android.os.UserHandle; 3226515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenenimport android.provider.Settings; 3300dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenenimport android.provider.Settings.SettingNotFoundException; 34fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenenimport android.util.Log; 35fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenenimport com.android.internal.content.PackageMonitor; 3626515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 3726515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenenimport java.util.ArrayList; 3826515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenenimport java.util.List; 3926515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 4026515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenenpublic class PaymentBackend { 4126515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen public static final String TAG = "Settings.PaymentBackend"; 4226515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 43fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public interface Callback { 44fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen void onPaymentAppsChanged(); 45fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 46fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 4726515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen public static class PaymentAppInfo { 48fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen CharSequence label; 49fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen CharSequence description; 50da6c0ba382c1c2c9b98f11a1ef9e572472654e27Martijn Coenen Drawable banner; 5126515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen boolean isDefault; 5226515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen public ComponentName componentName; 53fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public ComponentName settingsComponent; 5426515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen } 5526515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 5626515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen private final Context mContext; 5726515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen private final NfcAdapter mAdapter; 58a73aa32278adb177042d4b5c873a0f08deb1d3f3Martijn Coenen private final CardEmulation mCardEmuManager; 59fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen private final PackageMonitor mSettingsPackageMonitor = new SettingsPackageMonitor(); 60fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen // Fields below only modified on UI thread 61fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen private ArrayList<PaymentAppInfo> mAppInfos; 62fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen private PaymentAppInfo mDefaultAppInfo; 63fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen private ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); 6426515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 6526515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen public PaymentBackend(Context context) { 6626515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen mContext = context; 6726515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 6826515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen mAdapter = NfcAdapter.getDefaultAdapter(context); 69a73aa32278adb177042d4b5c873a0f08deb1d3f3Martijn Coenen mCardEmuManager = CardEmulation.getInstance(mAdapter); 70fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen refresh(); 7126515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen } 7226515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 73fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public void onPause() { 74fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen mSettingsPackageMonitor.unregister(); 75fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 76fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 77fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public void onResume() { 78fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen mSettingsPackageMonitor.register(mContext, mContext.getMainLooper(), false); 79fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 80fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 81fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public void refresh() { 8226515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen PackageManager pm = mContext.getPackageManager(); 8326515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen List<ApduServiceInfo> serviceInfos = 84a73aa32278adb177042d4b5c873a0f08deb1d3f3Martijn Coenen mCardEmuManager.getServices(CardEmulation.CATEGORY_PAYMENT); 85fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen ArrayList<PaymentAppInfo> appInfos = new ArrayList<PaymentAppInfo>(); 8626515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 87fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen if (serviceInfos == null) { 88fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen makeCallbacks(); 89fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen return; 90fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 9126515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 92fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen ComponentName defaultAppName = getDefaultPaymentApp(); 93fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen PaymentAppInfo foundDefaultApp = null; 9426515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen for (ApduServiceInfo service : serviceInfos) { 9526515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen PaymentAppInfo appInfo = new PaymentAppInfo(); 96fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen appInfo.label = service.loadLabel(pm); 97fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen if (appInfo.label == null) { 98fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen appInfo.label = service.loadAppLabel(pm); 99fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 100fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen appInfo.isDefault = service.getComponent().equals(defaultAppName); 101fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen if (appInfo.isDefault) { 102fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen foundDefaultApp = appInfo; 1030e940d6c00d06578c4c0f89a95e5d31c8073fbc1Martijn Coenen } 10426515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen appInfo.componentName = service.getComponent(); 105fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen String settingsActivity = service.getSettingsActivityName(); 106fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen if (settingsActivity != null) { 107fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen appInfo.settingsComponent = new ComponentName(appInfo.componentName.getPackageName(), 108fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen settingsActivity); 109fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } else { 110fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen appInfo.settingsComponent = null; 111fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 1126d5cac594620bd270f263fedfb8a23d6d916b014Martijn Coenen appInfo.description = service.getDescription(); 1136d5cac594620bd270f263fedfb8a23d6d916b014Martijn Coenen appInfo.banner = service.loadBanner(pm); 11426515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen appInfos.add(appInfo); 11526515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen } 116fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen mAppInfos = appInfos; 117fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen mDefaultAppInfo = foundDefaultApp; 118fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen makeCallbacks(); 119fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 120fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 121fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public void registerCallback(Callback callback) { 122fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen mCallbacks.add(callback); 123fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 124fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 125fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public void unregisterCallback(Callback callback) { 126fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen mCallbacks.remove(callback); 127fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 128fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 129fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public List<PaymentAppInfo> getPaymentAppInfos() { 130fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen return mAppInfos; 131fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 132fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 133fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public PaymentAppInfo getDefaultApp() { 134fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen return mDefaultAppInfo; 135fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 136fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 137fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen void makeCallbacks() { 138fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen for (Callback callback : mCallbacks) { 139fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen callback.onPaymentAppsChanged(); 140fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 141fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 142fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 143fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen Drawable loadDrawableForPackage(String pkgName, int drawableResId) { 144fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen PackageManager pm = mContext.getPackageManager(); 145fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen try { 146fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen Resources res = pm.getResourcesForApplication(pkgName); 147fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen Drawable banner = res.getDrawable(drawableResId); 148fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen return banner; 149fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } catch (Resources.NotFoundException e) { 150fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen return null; 151fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } catch (PackageManager.NameNotFoundException e) { 152fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen return null; 153fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 154fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 15526515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 15600dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen boolean isForegroundMode() { 15700dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen try { 15800dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen return Settings.Secure.getInt(mContext.getContentResolver(), 15900dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen Settings.Secure.NFC_PAYMENT_FOREGROUND) != 0; 16000dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen } catch (SettingNotFoundException e) { 16100dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen return false; 16200dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen } 16300dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen } 16400dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen 16500dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen void setForegroundMode(boolean foreground) { 16600dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen Settings.Secure.putInt(mContext.getContentResolver(), 16700dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen Settings.Secure.NFC_PAYMENT_FOREGROUND, foreground ? 1 : 0) ; 16800dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen } 16900dbb74b8d699510ece683fc7ab12fc2343fa5d5Martijn Coenen 17026515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen ComponentName getDefaultPaymentApp() { 17126515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen String componentString = Settings.Secure.getString(mContext.getContentResolver(), 17226515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT); 17326515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen if (componentString != null) { 17426515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen return ComponentName.unflattenFromString(componentString); 17526515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen } else { 17626515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen return null; 17726515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen } 17826515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen } 17926515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen 18026515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen public void setDefaultPaymentApp(ComponentName app) { 18126515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen Settings.Secure.putString(mContext.getContentResolver(), 18226515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen Settings.Secure.NFC_PAYMENT_DEFAULT_COMPONENT, 18326515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen app != null ? app.flattenToString() : null); 184fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen refresh(); 185fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 186fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 187fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen private final Handler mHandler = new Handler() { 188fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen @Override 189fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public void dispatchMessage(Message msg) { 190fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen refresh(); 191fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 192fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen }; 193fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 194fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen private class SettingsPackageMonitor extends PackageMonitor { 195fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen @Override 196fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public void onPackageAdded(String packageName, int uid) { 197fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen mHandler.obtainMessage().sendToTarget(); 198fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 199fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 200fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen @Override 201fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public void onPackageAppeared(String packageName, int reason) { 202fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen mHandler.obtainMessage().sendToTarget(); 203fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 204fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 205fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen @Override 206fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public void onPackageDisappeared(String packageName, int reason) { 207fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen mHandler.obtainMessage().sendToTarget(); 208fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 209fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen 210fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen @Override 211fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen public void onPackageRemoved(String packageName, int uid) { 212fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen mHandler.obtainMessage().sendToTarget(); 213fe58b534f6a1bf63dadb18dae13c59ed8a014eecMartijn Coenen } 21426515da08707b6f8182cc8b0ed5e01e97aa92a96Martijn Coenen } 2156d5cac594620bd270f263fedfb8a23d6d916b014Martijn Coenen} 216