gt: 							synchronized (authTokenRecords) {
context: package com.lody.virtual.server.accounts;
  
  import android.accounts.Account;
  import android.accounts.AccountManager;
  import android.accounts.AuthenticatorDescription;
  import android.accounts.IAccountAuthenticator;
  import android.accounts.IAccountAuthenticatorResponse;
  import android.accounts.IAccountManagerResponse;
  import android.content.ComponentName;
  import android.content.Context;
  import android.content.Intent;
  import android.content.ServiceConnection;
  import android.content.pm.PackageManager;
  import android.content.pm.ResolveInfo;
  import android.content.pm.ServiceInfo;
  import android.content.res.Resources;
  import android.content.res.TypedArray;
  import android.content.res.XmlResourceParser;
  import android.os.Binder;
  import android.os.Bundle;
  import android.os.IBinder;
  import android.os.Parcel;
  import android.os.RemoteException;
  import android.os.SystemClock;
  import android.text.TextUtils;
  import android.util.AttributeSet;
  import android.util.Log;
  import android.util.SparseArray;
  import android.util.Xml;
  
  import com.lody.virtual.client.core.VirtualCore;
  import com.lody.virtual.client.ipc.VActivityManager;
  import com.lody.virtual.helper.compat.AccountManagerCompat;
  import com.lody.virtual.helper.utils.VLog;
  import com.lody.virtual.os.VBinder;
  import com.lody.virtual.os.VEnvironment;
  import com.lody.virtual.server.pm.VPackageManagerService;
  import com.lody.virtual.server.IAccountManager;
  
  import org.xmlpull.v1.XmlPullParser;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.LinkedHashMap;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.concurrent.atomic.AtomicReference;
  
  import mirror.com.android.internal.R_Hide;
  
  import static android.accounts.AccountManager.ERROR_CODE_BAD_ARGUMENTS;
  
  
  /**
   * @author Lody
   */
  public class VAccountManagerService extends IAccountManager.Stub {
  
  	private static final AtomicReference<VAccountManagerService> sThis = new AtomicReference<>();
  
      private Context mContext = VirtualCore.get().getContext();
  
      private static final String TAG = VAccountManagerService.class.getSimpleName();
  
  	private final SparseArray<List<VAccount>> accountsByUserId = new SparseArray<>();
  
  	private final LinkedList<AuthTokenRecord> authTokenRecords = new LinkedList<>();
  
  	private final LinkedHashMap<String, Session> mSessions = new LinkedHashMap<>();
  
  	private final AuthenticatorCache cache = new AuthenticatorCache();
  
  	private long lastAccountChangeTime = 0;
  
  
  	public static VAccountManagerService get() {
  		return sThis.get();
  	}
  
  	public static void systemReady() {
  		VAccountManagerService service = new VAccountManagerService();
  		service.deserializeAllAccounts();
  		sThis.set(service);
  	}
  
  
  	private static AuthenticatorDescription parseAuthenticatorDescription(Resources resources, String packageName,
  			AttributeSet attributeSet) {
  		TypedArray array = resources.obtainAttributes(attributeSet, R_Hide.styleable.AccountAuthenticator.get());
  		try {
  			String accountType = array.getString(R_Hide.styleable.AccountAuthenticator_accountType.get());
  			int label = array.getResourceId(R_Hide.styleable.AccountAuthenticator_label.get(), 0);
  			int icon = array.getResourceId(R_Hide.styleable.AccountAuthenticator_icon.get(), 0);
  			int smallIcon = array.getResourceId(R_Hide.styleable.AccountAuthenticator_smallIcon.get(), 0);
  			int accountPreferences = array.getResourceId(R_Hide.styleable.AccountAuthenticator_accountPreferences.get(), 0);
  			boolean customTokens = array.getBoolean(R_Hide.styleable.AccountAuthenticator_customTokens.get(), false);
  			if (TextUtils.isEmpty(accountType)) {
  				return null;
  			}
  			return new AuthenticatorDescription(accountType, packageName, label, icon, smallIcon, accountPreferences,
  					customTokens);
  		} finally {
  			array.recycle();
  		}
  	}
  
  
  	@Override
  	public AuthenticatorDescription[] getAuthenticatorTypes(int userId) {
  		synchronized (cache) {
  			AuthenticatorDescription[] descArray = new AuthenticatorDescription[cache.authenticators.size()];
  			int i = 0;
  			for (AuthenticatorInfo info : cache.authenticators.values()) {
  				descArray[i] = info.desc;
  				i++;
  			}
  			return descArray;
  		}
  	}
  
  	@Override
  	public void getAccountsByFeatures(int userId, IAccountManagerResponse response, String type, String[] features) {
  		if (response == null) throw new IllegalArgumentException("response is null");
  		if (type == null) throw new IllegalArgumentException("accountType is null");
  		AuthenticatorInfo info = getAuthenticatorInfo(type);
  		if(info == null) {
  			Bundle bundle = new Bundle();
  			bundle.putParcelableArray(AccountManager.KEY_ACCOUNTS, new Account[0]);
  			try {
  				response.onResult(bundle);
  			} catch (RemoteException e) {
  				e.printStackTrace();
  			}
  			return;
  		}
  
  		if (features == null || features.length == 0) {
  			Bundle bundle = new Bundle();
  			bundle.putParcelableArray(AccountManager.KEY_ACCOUNTS, getAccounts(userId, type));
  			try {
  				response.onResult(bundle);
  			} catch (RemoteException e) {
  				e.printStackTrace();
  			}
  		} else {
  			new GetAccountsByTypeAndFeatureSession(response, userId, info, features).bind();
  		}
  	}
  
  	@Override
  	public final String getPreviousName(int userId, Account account) {
  		if (account == null) throw new IllegalArgumentException("account is null");
  		synchronized (accountsByUserId) {
  			String previousName = null;
  			VAccount vAccount = getAccount(userId, account);
  			if (vAccount != null) {
  				previousName = vAccount.previousName;
  			}
  			return previousName;
  		}
  	}
  
  
  	@Override
  	public Account[] getAccounts(int userId, String type) {
  		List<Account> accountList = getAccountList(userId, type);
  		return accountList.toArray(new Account[accountList.size()]);
  	}
  
  
  	private List<Account> getAccountList(int userId, String type) {
  		synchronized (accountsByUserId) {
  			List<Account> accounts = new ArrayList<>();
  			List<VAccount> vAccounts = accountsByUserId.get(userId);
  			if (vAccounts != null) {
  				for (VAccount vAccount : vAccounts) {
  					if (type == null || vAccount.type.equals(type)) {
  						accounts.add(new Account(vAccount.name, vAccount.type));
  					}
  				}
  			}
  			return accounts;
  		}
  	}
  
  	@Override
  	public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
  		try {
  			return super.onTransact(code, data, reply, flags);
  		} catch (Throwable e) {
  			e.printStackTrace();
  			throw e;
  		}
  	}
  
  	@Override
      public final void getAuthToken(final int userId, final IAccountManagerResponse response, final Account account, final String authTokenType, final boolean notifyOnAuthFailure, boolean expectActivityLaunch, final Bundle loginOptions) {
          if (response == null) {
              throw new IllegalArgumentException("response is null");
          }
          try {
              if (account == null) {
                  VLog.w(TAG, "getAuthToken called with null account");
                  response.onError(ERROR_CODE_BAD_ARGUMENTS, "account is null");
                  return;
              }
              if (authTokenType == null) {
                  VLog.w(TAG, "getAuthToken called with null authTokenType");
                  response.onError(ERROR_CODE_BAD_ARGUMENTS, "authTokenType is null");
                  return;
              }
          } catch (RemoteException e) {
              e.printStackTrace();
              return;
          }
          AuthenticatorInfo info = getAuthenticatorInfo(account.type);
          if(info == null) {
              try {
                  response.onError(ERROR_CODE_BAD_ARGUMENTS, "account.type does not exist");
              } catch(RemoteException e) {
                  e.printStackTrace();
              }
              return;
          }
          // Get the calling package. We will use it for the purpose of caching.
          final String callerPkg = loginOptions.getString(AccountManagerCompat.KEY_ANDROID_PACKAGE_NAME);
  		final boolean customTokens = info.desc.customTokens;
  
          loginOptions.putInt(AccountManager.KEY_CALLER_UID, VBinder.getCallingUid());
          loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
  		if (notifyOnAuthFailure) {
  			loginOptions.putBoolean(AccountManagerCompat.KEY_NOTIFY_ON_FAILURE, true);
  		}
  		if (!customTokens) {
  			VAccount vAccount;
  			synchronized (accountsByUserId) {
  				vAccount = getAccount(userId, account);
  			}
  			String authToken = vAccount != null ? vAccount.authTokens.get(authTokenType) : null;
  			if (authToken != null) {
  				Bundle result = new Bundle();
  				result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
  				result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
  				result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
  				onResult(response, result);
  				return;
  			}
  		}
  		if (customTokens) {
  			String authToken = getCustomAuthToken(userId, account, authTokenType, callerPkg);
  			if (authToken != null) {
  				Bundle result = new Bundle();
  				result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
  				result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
  				result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
  				onResult(response, result);
  				return;
  			}
  		}
  		new Session(response, userId, info, expectActivityLaunch, false, account.name) {
  
  			@Override
  			protected String toDebugString(long now) {
  				return super.toDebugString(now) + ", getAuthToken"
  						+ ", " + account
  						+ ", authTokenType " + authTokenType
  						+ ", loginOptions " + loginOptions
  						+ ", notifyOnAuthFailure " + notifyOnAuthFailure;
  			}
  
  			@Override
  			public void run() throws RemoteException {
  				mAuthenticator.getAuthToken(this, account, authTokenType, loginOptions);
  			}
  
  			@Override
  			public void onResult(Bundle result) throws RemoteException {
  				if (result != null) {
  					String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
  					if (authToken != null) {
  						String name = result.getString(AccountManager.KEY_ACCOUNT_NAME);
  						String type = result.getString(AccountManager.KEY_ACCOUNT_TYPE);
  						if (TextUtils.isEmpty(type) || TextUtils.isEmpty(name)) {
  							onError(AccountManager.ERROR_CODE_INVALID_RESPONSE,
  									"the type and name should not be empty");
  							return;
  						}
  						if (!customTokens) {
  							synchronized (accountsByUserId) {
  								VAccount account = getAccount(userId, name, type);
  								if (account == null) {
  									List<VAccount> accounts = accountsByUserId.get(userId);
  									if (accounts == null) {
  										accounts = new ArrayList<>();
  										accountsByUserId.put(userId, accounts);
  									}
  									account = new VAccount(userId, new Account(name, type));
  									accounts.add(account);
  									serializeAllAccounts();
  								}
  							}
  						}
  						long expiryMillis = result.getLong(
  								AccountManagerCompat.KEY_CUSTOM_TOKEN_EXPIRY, 0L);
  						if (customTokens
  								&& expiryMillis > System.currentTimeMillis()) {
  							AuthTokenRecord record = new AuthTokenRecord(userId, account, authTokenType, callerPkg, authToken, expiryMillis);
  