1 /*
2  * Copyright (C) 2008 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.settings.accounts;
18 
19 import android.accounts.Account;
20 import android.accounts.AccountManager;
21 import android.app.Activity;
22 import android.app.Dialog;
23 import android.app.settings.SettingsEnums;
24 import android.content.ContentResolver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentSender;
28 import android.content.SyncAdapterType;
29 import android.content.SyncInfo;
30 import android.content.SyncStatusInfo;
31 import android.content.pm.PackageManager;
32 import android.content.pm.ProviderInfo;
33 import android.content.pm.UserInfo;
34 import android.os.Binder;
35 import android.os.Bundle;
36 import android.os.UserHandle;
37 import android.os.UserManager;
38 import android.text.TextUtils;
39 import android.text.format.DateUtils;
40 import android.util.Log;
41 import android.view.Menu;
42 import android.view.MenuInflater;
43 import android.view.MenuItem;
44 
45 import androidx.annotation.VisibleForTesting;
46 import androidx.appcompat.app.AlertDialog;
47 import androidx.preference.Preference;
48 
49 import com.android.settings.R;
50 import com.android.settings.Utils;
51 import com.android.settings.widget.EntityHeaderController;
52 import com.android.settingslib.widget.FooterPreference;
53 
54 import com.google.android.collect.Lists;
55 
56 import java.util.ArrayList;
57 import java.util.Date;
58 import java.util.HashMap;
59 import java.util.List;
60 
61 public class AccountSyncSettings extends AccountPreferenceBase {
62 
63     public static final String ACCOUNT_KEY = "account";
64     private static final int MENU_SYNC_NOW_ID = Menu.FIRST;
65     private static final int MENU_SYNC_CANCEL_ID = Menu.FIRST + 1;
66     private static final int CANT_DO_ONETIME_SYNC_DIALOG = 102;
67     private static final String UID_REQUEST_KEY = "uid_request_code";
68 
69     private Account mAccount;
70     private ArrayList<SyncAdapterType> mInvisibleAdapters = Lists.newArrayList();
71     private HashMap<Integer, Integer> mUidRequestCodeMap = new HashMap<>();
72 
73     @Override
onCreateDialog(final int id)74     public Dialog onCreateDialog(final int id) {
75         Dialog dialog = null;
76         if (id == CANT_DO_ONETIME_SYNC_DIALOG) {
77             dialog = new AlertDialog.Builder(getActivity())
78                     .setTitle(R.string.cant_sync_dialog_title)
79                     .setMessage(R.string.cant_sync_dialog_message)
80                     .setPositiveButton(android.R.string.ok, null)
81                     .create();
82         }
83         return dialog;
84     }
85 
86     @Override
getMetricsCategory()87     public int getMetricsCategory() {
88         return SettingsEnums.ACCOUNTS_ACCOUNT_SYNC;
89     }
90 
91     @Override
getDialogMetricsCategory(int dialogId)92     public int getDialogMetricsCategory(int dialogId) {
93         switch (dialogId) {
94             case CANT_DO_ONETIME_SYNC_DIALOG:
95                 return SettingsEnums.DIALOG_ACCOUNT_SYNC_CANNOT_ONETIME_SYNC;
96             default:
97                 return 0;
98         }
99     }
100 
101     @Override
onCreate(Bundle icicle)102     public void onCreate(Bundle icicle) {
103         super.onCreate(icicle);
104         addPreferencesFromResource(R.xml.account_sync_settings);
105         getPreferenceScreen().setOrderingAsAdded(false);
106         setAccessibilityTitle();
107     }
108 
109     @Override
onSaveInstanceState(Bundle outState)110     public void onSaveInstanceState(Bundle outState) {
111         super.onSaveInstanceState(outState);
112         if (!mUidRequestCodeMap.isEmpty()) {
113             outState.putSerializable(UID_REQUEST_KEY, mUidRequestCodeMap);
114         }
115     }
116 
117     @Override
onActivityCreated(Bundle savedInstanceState)118     public void onActivityCreated(Bundle savedInstanceState) {
119         super.onActivityCreated(savedInstanceState);
120 
121         Bundle arguments = getArguments();
122         if (arguments == null) {
123             Log.e(TAG, "No arguments provided when starting intent. ACCOUNT_KEY needed.");
124             finish();
125             return;
126         }
127         mAccount = arguments.getParcelable(ACCOUNT_KEY);
128         if (!accountExists(mAccount)) {
129             Log.e(TAG, "Account provided does not exist: " + mAccount);
130             finish();
131             return;
132         }
133         if (Log.isLoggable(TAG, Log.VERBOSE)) {
134             Log.v(TAG, "Got account: " + mAccount);
135         }
136         final Activity activity = getActivity();
137         final Preference pref = EntityHeaderController
138                 .newInstance(activity, this, null /* header */)
139                 .setRecyclerView(getListView(), getSettingsLifecycle())
140                 .setIcon(getDrawableForType(mAccount.type))
141                 .setLabel(mAccount.name)
142                 .setSummary(getLabelForType(mAccount.type))
143                 .done(activity, getPrefContext());
144         pref.setOrder(0);
145         getPreferenceScreen().addPreference(pref);
146         if (savedInstanceState != null && savedInstanceState.containsKey(UID_REQUEST_KEY)) {
147             mUidRequestCodeMap = (HashMap<Integer, Integer>) savedInstanceState.getSerializable(
148                     UID_REQUEST_KEY);
149         }
150     }
151 
setAccessibilityTitle()152     private void setAccessibilityTitle() {
153         final UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
154         UserInfo user = um.getUserInfo(mUserHandle.getIdentifier());
155         boolean isWorkProfile = user != null ? user.isManagedProfile() : false;
156         CharSequence currentTitle = getActivity().getTitle();
157         String accessibilityTitle =
158                 getString(isWorkProfile
159                         ? R.string.accessibility_work_account_title
160                         : R.string.accessibility_personal_account_title, currentTitle);
161         getActivity().setTitle(Utils.createAccessibleSequence(currentTitle, accessibilityTitle));
162     }
163 
164     @Override
onResume()165     public void onResume() {
166         mAuthenticatorHelper.listenToAccountUpdates();
167         updateAuthDescriptions();
168         onAccountsUpdate(Binder.getCallingUserHandle());
169         super.onResume();
170     }
171 
172     @Override
onPause()173     public void onPause() {
174         super.onPause();
175         mAuthenticatorHelper.stopListeningToAccountUpdates();
176     }
177 
addSyncStateSwitch(Account account, String authority, String packageName, int uid)178     private void addSyncStateSwitch(Account account, String authority,
179             String packageName, int uid) {
180         SyncStateSwitchPreference item = (SyncStateSwitchPreference) getCachedPreference(authority);
181         if (item == null) {
182             item = new SyncStateSwitchPreference(getPrefContext(), account, authority,
183                     packageName, uid);
184             getPreferenceScreen().addPreference(item);
185         } else {
186             item.setup(account, authority, packageName, uid);
187         }
188         final PackageManager packageManager = getPackageManager();
189         item.setPersistent(false);
190         final ProviderInfo providerInfo = packageManager.resolveContentProviderAsUser(
191                 authority, 0, mUserHandle.getIdentifier());
192         if (providerInfo == null) {
193             return;
194         }
195         final CharSequence providerLabel = providerInfo.loadLabel(packageManager);
196         if (TextUtils.isEmpty(providerLabel)) {
197             Log.e(TAG, "Provider needs a label for authority '" + authority + "'");
198             return;
199         }
200         item.setTitle(providerLabel);
201         item.setKey(authority);
202     }
203 
204     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)205     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
206         MenuItem syncNow = menu.add(0, MENU_SYNC_NOW_ID, 0,
207                 getString(R.string.sync_menu_sync_now));
208         MenuItem syncCancel = menu.add(0, MENU_SYNC_CANCEL_ID, 0,
209                 getString(R.string.sync_menu_sync_cancel));
210 
211         syncNow.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
212                 MenuItem.SHOW_AS_ACTION_WITH_TEXT);
213         syncCancel.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER |
214                 MenuItem.SHOW_AS_ACTION_WITH_TEXT);
215 
216         super.onCreateOptionsMenu(menu, inflater);
217     }
218 
219     @Override
onPrepareOptionsMenu(Menu menu)220     public void onPrepareOptionsMenu(Menu menu) {
221         super.onPrepareOptionsMenu(menu);
222         // Note that this also counts accounts that are not currently displayed
223         boolean syncActive = !ContentResolver.getCurrentSyncsAsUser(
224                 mUserHandle.getIdentifier()).isEmpty();
225         menu.findItem(MENU_SYNC_NOW_ID).setVisible(!syncActive).setEnabled(enabledSyncNowMenu());
226         menu.findItem(MENU_SYNC_CANCEL_ID).setVisible(syncActive);
227     }
228 
229     @Override
onOptionsItemSelected(MenuItem item)230     public boolean onOptionsItemSelected(MenuItem item) {
231         switch (item.getItemId()) {
232             case MENU_SYNC_NOW_ID:
233                 startSyncForEnabledProviders();
234                 return true;
235             case MENU_SYNC_CANCEL_ID:
236                 cancelSyncForEnabledProviders();
237                 return true;
238         }
239         return super.onOptionsItemSelected(item);
240     }
241 
242     @Override
onActivityResult(int requestCode, int resultCode, Intent data)243     public void onActivityResult(int requestCode, int resultCode, Intent data) {
244         if (resultCode == Activity.RESULT_OK) {
245             final int count = getPreferenceScreen().getPreferenceCount();
246             for (int i = 0; i < count; i++) {
247                 Preference preference = getPreferenceScreen().getPreference(i);
248                 if (preference instanceof SyncStateSwitchPreference) {
249                     SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) preference;
250                     if (getRequestCodeByUid(syncPref.getUid()) == requestCode) {
251                         onPreferenceTreeClick(syncPref);
252                         return;
253                     }
254                 }
255             }
256         }
257     }
258 
259     @Override
onPreferenceTreeClick(Preference preference)260     public boolean onPreferenceTreeClick(Preference preference) {
261         if (getActivity() == null) {
262             return false;
263         }
264         if (preference instanceof SyncStateSwitchPreference) {
265             SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) preference;
266             final String authority = syncPref.getAuthority();
267             if (TextUtils.isEmpty(authority)) {
268                 return false;
269             }
270             final Account account = syncPref.getAccount();
271             final int userId = mUserHandle.getIdentifier();
272             final String packageName = syncPref.getPackageName();
273 
274             boolean syncAutomatically = ContentResolver.getSyncAutomaticallyAsUser(account,
275                     authority, userId);
276             if (syncPref.isOneTimeSyncMode()) {
277                 // If the sync adapter doesn't have access to the account we either
278                 // request access by starting an activity if possible or kick off the
279                 // sync which will end up posting an access request notification.
280                 if (requestAccountAccessIfNeeded(packageName)) {
281                     return true;
282                 }
283                 requestOrCancelSync(account, authority, true);
284             } else {
285                 boolean syncOn = syncPref.isChecked();
286                 boolean oldSyncState = syncAutomatically;
287                 if (syncOn != oldSyncState) {
288                     // Toggling this switch triggers sync but we may need a user approval.
289                     // If the sync adapter doesn't have access to the account we either
290                     // request access by starting an activity if possible or kick off the
291                     // sync which will end up posting an access request notification.
292                     if (syncOn && requestAccountAccessIfNeeded(packageName)) {
293                         return true;
294                     }
295                     // if we're enabling sync, this will request a sync as well
296                     ContentResolver.setSyncAutomaticallyAsUser(account, authority, syncOn, userId);
297                     // if the master sync switch is off, the request above will
298                     // get dropped.  when the user clicks on this toggle,
299                     // we want to force the sync, however.
300                     if (!ContentResolver.getMasterSyncAutomaticallyAsUser(userId) || !syncOn) {
301                         requestOrCancelSync(account, authority, syncOn);
302                     }
303                 }
304             }
305             return true;
306         } else {
307             return super.onPreferenceTreeClick(preference);
308         }
309     }
310 
requestAccountAccessIfNeeded(String packageName)311     private boolean requestAccountAccessIfNeeded(String packageName) {
312         if (packageName == null) {
313             return false;
314         }
315 
316         final int uid;
317         try {
318             uid = getContext().getPackageManager().getPackageUidAsUser(
319                     packageName, mUserHandle.getIdentifier());
320         } catch (PackageManager.NameNotFoundException e) {
321             Log.e(TAG, "Invalid sync ", e);
322             return false;
323         }
324 
325         AccountManager accountManager = getContext().getSystemService(AccountManager.class);
326         if (!accountManager.hasAccountAccess(mAccount, packageName, mUserHandle)) {
327             IntentSender intent = accountManager.createRequestAccountAccessIntentSenderAsUser(
328                     mAccount, packageName, mUserHandle);
329             if (intent != null) {
330                 try {
331                     final int requestCode = addUidAndGenerateRequestCode(uid);
332                     startIntentSenderForResult(intent, requestCode, null /* fillInIntent */, 0, 0,
333                             0, null /* options */);
334                     return true;
335                 } catch (IntentSender.SendIntentException e) {
336                     Log.e(TAG, "Error requesting account access", e);
337                 }
338             }
339         }
340         return false;
341     }
342 
startSyncForEnabledProviders()343     private void startSyncForEnabledProviders() {
344         requestOrCancelSyncForEnabledProviders(true /* start them */);
345         final Activity activity = getActivity();
346         if (activity != null) {
347             activity.invalidateOptionsMenu();
348         }
349     }
350 
cancelSyncForEnabledProviders()351     private void cancelSyncForEnabledProviders() {
352         requestOrCancelSyncForEnabledProviders(false /* cancel them */);
353         final Activity activity = getActivity();
354         if (activity != null) {
355             activity.invalidateOptionsMenu();
356         }
357     }
358 
requestOrCancelSyncForEnabledProviders(boolean startSync)359     private void requestOrCancelSyncForEnabledProviders(boolean startSync) {
360         // sync everything that the user has enabled
361         int count = getPreferenceScreen().getPreferenceCount();
362         for (int i = 0; i < count; i++) {
363             Preference pref = getPreferenceScreen().getPreference(i);
364             if (!(pref instanceof SyncStateSwitchPreference)) {
365                 continue;
366             }
367             SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref;
368             if (!syncPref.isChecked()) {
369                 continue;
370             }
371             requestOrCancelSync(syncPref.getAccount(), syncPref.getAuthority(), startSync);
372         }
373         // plus whatever the system needs to sync, e.g., invisible sync adapters
374         if (mAccount != null) {
375             for (SyncAdapterType syncAdapter : mInvisibleAdapters) {
376                 requestOrCancelSync(mAccount, syncAdapter.authority, startSync);
377             }
378         }
379     }
380 
requestOrCancelSync(Account account, String authority, boolean flag)381     private void requestOrCancelSync(Account account, String authority, boolean flag) {
382         if (flag) {
383             Bundle extras = new Bundle();
384             extras.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
385             ContentResolver.requestSyncAsUser(account, authority, mUserHandle.getIdentifier(),
386                     extras);
387         } else {
388             ContentResolver.cancelSyncAsUser(account, authority, mUserHandle.getIdentifier());
389         }
390     }
391 
isSyncing(List<SyncInfo> currentSyncs, Account account, String authority)392     private boolean isSyncing(List<SyncInfo> currentSyncs, Account account, String authority) {
393         for (SyncInfo syncInfo : currentSyncs) {
394             if (syncInfo.account.equals(account) && syncInfo.authority.equals(authority)) {
395                 return true;
396             }
397         }
398         return false;
399     }
400 
401     @Override
onSyncStateUpdated()402     protected void onSyncStateUpdated() {
403         if (!isResumed()) return;
404         setFeedsState();
405         final Activity activity = getActivity();
406         if (activity != null) {
407             activity.invalidateOptionsMenu();
408         }
409     }
410 
setFeedsState()411     private void setFeedsState() {
412         // iterate over all the preferences, setting the state properly for each
413         Date date = new Date();
414         final int userId = mUserHandle.getIdentifier();
415         List<SyncInfo> currentSyncs = ContentResolver.getCurrentSyncsAsUser(userId);
416         boolean syncIsFailing = false;
417 
418         // Refresh the sync status switches - some syncs may have become active.
419         updateAccountSwitches();
420 
421         for (int i = 0, count = getPreferenceScreen().getPreferenceCount(); i < count; i++) {
422             Preference pref = getPreferenceScreen().getPreference(i);
423             if (!(pref instanceof SyncStateSwitchPreference)) {
424                 continue;
425             }
426             SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref;
427 
428             String authority = syncPref.getAuthority();
429             Account account = syncPref.getAccount();
430 
431             SyncStatusInfo status = ContentResolver.getSyncStatusAsUser(account, authority, userId);
432             boolean syncEnabled = ContentResolver.getSyncAutomaticallyAsUser(account, authority,
433                     userId);
434             boolean authorityIsPending = status == null ? false : status.pending;
435             boolean initialSync = status == null ? false : status.initialize;
436 
437             boolean activelySyncing = isSyncing(currentSyncs, account, authority);
438             boolean lastSyncFailed = status != null
439                     && status.lastFailureTime != 0
440                     && status.getLastFailureMesgAsInt(0)
441                     != ContentResolver.SYNC_ERROR_SYNC_ALREADY_IN_PROGRESS;
442             if (!syncEnabled) lastSyncFailed = false;
443             if (lastSyncFailed && !activelySyncing && !authorityIsPending) {
444                 syncIsFailing = true;
445             }
446             if (Log.isLoggable(TAG, Log.DEBUG)) {
447                 Log.d(TAG, "Update sync status: " + account + " " + authority +
448                         " active = " + activelySyncing + " pend =" + authorityIsPending);
449             }
450 
451             final long successEndTime = (status == null) ? 0 : status.lastSuccessTime;
452             if (!syncEnabled) {
453                 syncPref.setSummary(R.string.sync_disabled);
454             } else if (activelySyncing) {
455                 syncPref.setSummary(R.string.sync_in_progress);
456             } else if (successEndTime != 0) {
457                 date.setTime(successEndTime);
458                 final String timeString = formatSyncDate(getContext(), date);
459                 syncPref.setSummary(getResources().getString(R.string.last_synced, timeString));
460             } else {
461                 syncPref.setSummary("");
462             }
463             int syncState = ContentResolver.getIsSyncableAsUser(account, authority, userId);
464 
465             syncPref.setActive(activelySyncing && (syncState >= 0) &&
466                     !initialSync);
467             syncPref.setPending(authorityIsPending && (syncState >= 0) &&
468                     !initialSync);
469 
470             syncPref.setFailed(lastSyncFailed);
471             final boolean oneTimeSyncMode = !ContentResolver.getMasterSyncAutomaticallyAsUser(
472                     userId);
473             syncPref.setOneTimeSyncMode(oneTimeSyncMode);
474             syncPref.setChecked(oneTimeSyncMode || syncEnabled);
475         }
476         if (syncIsFailing) {
477             getPreferenceScreen().addPreference(new FooterPreference.Builder(
478                     getActivity()).setTitle(R.string.sync_is_failing).build());
479         }
480     }
481 
482     @Override
onAccountsUpdate(final UserHandle userHandle)483     public void onAccountsUpdate(final UserHandle userHandle) {
484         super.onAccountsUpdate(userHandle);
485         if (!accountExists(mAccount)) {
486             // The account was deleted
487             finish();
488             return;
489         }
490         updateAccountSwitches();
491         onSyncStateUpdated();
492     }
493 
accountExists(Account account)494     private boolean accountExists(Account account) {
495         if (account == null) return false;
496 
497         Account[] accounts = AccountManager.get(getActivity()).getAccountsByTypeAsUser(
498                 account.type, mUserHandle);
499         final int count = accounts.length;
500         for (int i = 0; i < count; i++) {
501             if (accounts[i].equals(account)) {
502                 return true;
503             }
504         }
505         return false;
506     }
507 
updateAccountSwitches()508     private void updateAccountSwitches() {
509         mInvisibleAdapters.clear();
510 
511         SyncAdapterType[] syncAdapters = ContentResolver.getSyncAdapterTypesAsUser(
512                 mUserHandle.getIdentifier());
513         ArrayList<SyncAdapterType> authorities = new ArrayList<>();
514         for (int i = 0, n = syncAdapters.length; i < n; i++) {
515             final SyncAdapterType sa = syncAdapters[i];
516             // Only keep track of sync adapters for this account
517             if (!sa.accountType.equals(mAccount.type)) continue;
518             if (sa.isUserVisible()) {
519                 if (Log.isLoggable(TAG, Log.DEBUG)) {
520                     Log.d(TAG, "updateAccountSwitches: added authority " + sa.authority
521                             + " to accountType " + sa.accountType);
522                 }
523                 authorities.add(sa);
524             } else {
525                 // keep track of invisible sync adapters, so sync now forces
526                 // them to sync as well.
527                 mInvisibleAdapters.add(sa);
528             }
529         }
530 
531         if (Log.isLoggable(TAG, Log.DEBUG)) {
532             Log.d(TAG, "looking for sync adapters that match account " + mAccount);
533         }
534 
535         cacheRemoveAllPrefs(getPreferenceScreen());
536         getCachedPreference(EntityHeaderController.PREF_KEY_APP_HEADER);
537         for (int j = 0, m = authorities.size(); j < m; j++) {
538             final SyncAdapterType syncAdapter = authorities.get(j);
539             // We could check services here....
540             int syncState = ContentResolver.getIsSyncableAsUser(mAccount, syncAdapter.authority,
541                     mUserHandle.getIdentifier());
542             if (Log.isLoggable(TAG, Log.DEBUG)) {
543                 Log.d(TAG, "  found authority " + syncAdapter.authority + " " + syncState);
544             }
545             if (syncState > 0) {
546                 final int uid;
547                 try {
548                     uid = getContext().getPackageManager().getPackageUidAsUser(
549                             syncAdapter.getPackageName(), mUserHandle.getIdentifier());
550                     addSyncStateSwitch(mAccount, syncAdapter.authority,
551                             syncAdapter.getPackageName(), uid);
552                 } catch (PackageManager.NameNotFoundException e) {
553                     Log.e(TAG, "No uid for package" + syncAdapter.getPackageName(), e);
554                 }
555             }
556         }
557         removeCachedPrefs(getPreferenceScreen());
558     }
559 
560     @Override
getHelpResource()561     public int getHelpResource() {
562         return R.string.help_url_accounts;
563     }
564 
565     @VisibleForTesting
enabledSyncNowMenu()566     boolean enabledSyncNowMenu() {
567         boolean enabled = false;
568         for (int i = 0, count = getPreferenceScreen().getPreferenceCount(); i < count; i++) {
569             final Preference pref = getPreferenceScreen().getPreference(i);
570             if (!(pref instanceof SyncStateSwitchPreference)) {
571                 continue;
572             }
573             final SyncStateSwitchPreference syncPref = (SyncStateSwitchPreference) pref;
574             if (syncPref.isChecked()) {
575                 enabled = true;
576                 break;
577             }
578         }
579         return enabled;
580     }
581 
formatSyncDate(Context context, Date date)582     private static String formatSyncDate(Context context, Date date) {
583         return DateUtils.formatDateTime(context, date.getTime(),
584                 DateUtils.FORMAT_SHOW_DATE
585                         | DateUtils.FORMAT_SHOW_YEAR
586                         | DateUtils.FORMAT_SHOW_TIME);
587     }
588 
addUidAndGenerateRequestCode(int uid)589     private int addUidAndGenerateRequestCode(int uid) {
590         if (mUidRequestCodeMap.containsKey(uid)) {
591             return mUidRequestCodeMap.get(uid);
592         }
593         final int requestCode = mUidRequestCodeMap.size() + 1;
594         mUidRequestCodeMap.put(uid, requestCode);
595         return requestCode;
596     }
597 
getRequestCodeByUid(int uid)598     private int getRequestCodeByUid(int uid) {
599         if (!mUidRequestCodeMap.containsKey(uid)) {
600             return -1;
601         }
602         return mUidRequestCodeMap.get(uid);
603     }
604 }
605