void setPara(CharSequence text, int start, int end, TextDirectionHeuristic textDir) { this.mText = text; this.mTextStart = start; int len = end - start; this.mLen = len; this.mPos = 0; if (this.mWidths == null || this.mWidths.length < len) { this.mWidths = ArrayUtils.newUnpaddedFloatArray(len); } if (this.mChars == null || this.mChars.length < len) { this.mChars = ArrayUtils.newUnpaddedCharArray(len); } TextUtils.getChars(text, start, end, this.mChars, 0); if (text instanceof Spanned) { Spanned spanned = (Spanned) text; ReplacementSpan[] spans = (ReplacementSpan[]) spanned.getSpans(start, end, ReplacementSpan.class); for (int i = 0; i < spans.length; i++) { int startInPara = spanned.getSpanStart(spans[i]) - start; int endInPara = spanned.getSpanEnd(spans[i]) - start; if (startInPara < 0) { startInPara = 0; } if (endInPara > len) { endInPara = len; } for (int j = startInPara; j < endInPara; j++) { this.mChars[j] = '\ufffc'; } } } if ((textDir == TextDirectionHeuristics.LTR || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR || textDir == TextDirectionHeuristics.ANYRTL_LTR) && TextUtils.doesNotNeedBidi(this.mChars, 0, len)) { this.mDir = 1; this.mEasy = true; return; } int bidiRequest; if (this.mLevels == null || this.mLevels.length < len) { this.mLevels = ArrayUtils.newUnpaddedByteArray(len); } if (textDir == TextDirectionHeuristics.LTR) { bidiRequest = 1; } else if (textDir == TextDirectionHeuristics.RTL) { bidiRequest = -1; } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) { bidiRequest = 2; } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) { bidiRequest = -2; } else { bidiRequest = textDir.isRtl(this.mChars, 0, len) ? -1 : 1; } this.mDir = AndroidBidi.bidi(bidiRequest, this.mChars, this.mLevels, len, false); this.mEasy = false; }
public void setInfo(String itemKey, boolean isVertical) { ButtonInfo item = NavbarEditor.buttonMap.get(itemKey); setTag(itemKey); final Resources res = getResources(); setContentDescription(res.getString(item.contentDescription)); mCode = item.keyCode; boolean isSmallButton = ArrayUtils.contains(NavbarEditor.smallButtonIds, getId()); Drawable keyD; if (isSmallButton) { keyD = res.getDrawable(item.sideResource); } else if (!isVertical) { keyD = res.getDrawable(item.portResource); } else { keyD = res.getDrawable(item.landResource); } // Reason for setImageDrawable vs setImageResource is because setImageResource calls relayout() // w/o // any checks. setImageDrawable performs size checks and only calls relayout if necessary. We // rely on this // because otherwise the setX/setY attributes which are post layout cause it to mess up the // layout. setImageDrawable(keyD); if (itemKey.equals(NavbarEditor.NAVBAR_EMPTY)) { if (isSmallButton) { setVisibility(NavigationBarView.getEditMode() ? View.VISIBLE : View.INVISIBLE); } else { setVisibility(NavigationBarView.getEditMode() ? View.VISIBLE : View.GONE); } } else if (itemKey.equals(NavbarEditor.NAVBAR_CONDITIONAL_MENU)) { setVisibility(NavigationBarView.getEditMode() ? View.VISIBLE : View.INVISIBLE); } else if (itemKey.equals(NavbarEditor.NAVBAR_HOME)) { mSupportsLongpress = false; } }
/** * Initializes a TextLine and prepares it for use. * * @param paint the base paint for the line * @param text the text, can be Styled * @param start the start of the line relative to the text * @param limit the limit of the line relative to the text * @param dir the paragraph direction of this line * @param directions the directions information of this line * @param hasTabs true if the line might contain tabs or emoji * @param tabStops the tabStops. Can be null. */ void set( TextPaint paint, CharSequence text, int start, int limit, int dir, Directions directions, boolean hasTabs, TabStops tabStops) { mPaint = paint; mText = text; mStart = start; mLen = limit - start; mDir = dir; mDirections = directions; if (mDirections == null) { throw new IllegalArgumentException("Directions cannot be null"); } mHasTabs = hasTabs; mSpanned = null; boolean hasReplacement = false; if (text instanceof Spanned) { mSpanned = (Spanned) text; mReplacementSpanSpanSet.init(mSpanned, start, limit); hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0; } mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT; if (mCharsValid) { if (mChars == null || mChars.length < mLen) { mChars = new char[ArrayUtils.idealCharArraySize(mLen)]; } TextUtils.getChars(text, start, limit, mChars, 0); if (hasReplacement) { // Handle these all at once so we don't have to do it as we go. // Replace the first character of each replacement run with the // object-replacement character and the remainder with zero width // non-break space aka BOM. Cursor movement code skips these // zero-width characters. char[] chars = mChars; for (int i = start, inext; i < limit; i = inext) { inext = mReplacementSpanSpanSet.getNextTransition(i, limit); if (mReplacementSpanSpanSet.hasSpansIntersecting(i, inext)) { // transition into a span chars[i - start] = '\ufffc'; for (int j = i - start + 1, e = inext - start; j < e; ++j) { chars[j] = '\ufeff'; // used as ZWNBS, marks positions to skip } } } } } mTabs = tabStops; }
private static List<VpnProfile> loadVpnProfiles(KeyStore keyStore, int... excludeTypes) { final ArrayList<VpnProfile> result = Lists.newArrayList(); final String[] keys = keyStore.saw(Credentials.VPN); if (keys != null) { for (String key : keys) { final VpnProfile profile = VpnProfile.decode(key, keyStore.get(Credentials.VPN + key)); if (profile != null && !ArrayUtils.contains(excludeTypes, profile.type)) { result.add(profile); } } } return result; }
private void updateModeSummary() { int mode = Settings.System.getIntForUser( getContentResolver(), Settings.System.DISPLAY_TEMPERATURE_MODE, MODE_OFF, UserHandle.USER_CURRENT); int index = ArrayUtils.indexOf(mModeValues, String.valueOf(mode)); mLiveDisplay.setSummary(mModeSummaries[index]); if (mDisplayTemperature != null) { mDisplayTemperature.setEnabled(mode != MODE_OFF); } if (mOutdoorMode != null) { mOutdoorMode.setEnabled(mode != MODE_OFF); } }
private void updateHeaderList(List<Header> target) { final boolean showDev = mDevelopmentPreferences.getBoolean( DevelopmentSettings.PREF_SHOW, android.os.Build.TYPE.equals("eng") || android.os.Build.TYPE.equals("userdebug")); int i = 0; mHeaderIndexMap.clear(); while (i < target.size()) { Header header = target.get(i); // Ids are integers, so downcasting int id = (int) header.id; if (id == R.id.operator_settings || id == R.id.manufacturer_settings || id == R.id.advanced_settings || id == R.id.hybrid_settings) { Utils.updateHeaderToSpecificActivityFromMetaDataOrRemove(this, target, header); } else if (id == R.id.launcher_settings) { Intent launcherIntent = new Intent(Intent.ACTION_MAIN); launcherIntent.addCategory(Intent.CATEGORY_HOME); launcherIntent.addCategory(Intent.CATEGORY_DEFAULT); Intent launcherPreferencesIntent = new Intent(Intent.ACTION_MAIN); launcherPreferencesIntent.addCategory("com.cyanogenmod.category.LAUNCHER_PREFERENCES"); ActivityInfo defaultLauncher = getPackageManager() .resolveActivity(launcherIntent, PackageManager.MATCH_DEFAULT_ONLY) .activityInfo; launcherPreferencesIntent.setPackage(defaultLauncher.packageName); ResolveInfo launcherPreferences = getPackageManager().resolveActivity(launcherPreferencesIntent, 0); if (launcherPreferences != null) { header.intent = new Intent() .setClassName( launcherPreferences.activityInfo.packageName, launcherPreferences.activityInfo.name); } else { target.remove(header); } } else if (id == R.id.wifi_settings) { // Remove WiFi Settings if WiFi service is not available. if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI)) { target.remove(i); } } else if (id == R.id.bluetooth_settings) { // Remove Bluetooth Settings if Bluetooth service is not available. if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH)) { target.remove(i); } } else if (id == R.id.data_usage_settings) { // Remove data usage when kernel module not enabled final INetworkManagementService netManager = INetworkManagementService.Stub.asInterface( ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE)); try { if (!netManager.isBandwidthControlEnabled()) { target.remove(i); } } catch (RemoteException e) { // ignored } } else if (id == R.id.account_settings) { int headerIndex = i + 1; i = insertAccountsHeaders(target, headerIndex); } else if (id == R.id.user_settings) { if (!UserHandle.MU_ENABLED || !UserManager.supportsMultipleUsers() || Utils.isMonkeyRunning()) { target.remove(i); } } if (target.get(i) == header && UserHandle.MU_ENABLED && UserHandle.myUserId() != 0 && !ArrayUtils.contains(SETTINGS_FOR_RESTRICTED, id)) { target.remove(i); } // Increment if the current one wasn't removed by the Utils code. if (target.get(i) == header) { // Hold on to the first header, when we need to reset to the top-level if (mFirstHeader == null && HeaderAdapter.getHeaderType(header) != HeaderAdapter.HEADER_TYPE_CATEGORY) { mFirstHeader = header; } mHeaderIndexMap.put(id, i); i++; } } }
@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Activity activity = getActivity(); final ContentResolver resolver = activity.getContentResolver(); final Resources res = getResources(); mDefaultDayTemperature = res.getInteger(com.android.internal.R.integer.config_dayColorTemperature); mDefaultNightTemperature = res.getInteger(com.android.internal.R.integer.config_nightColorTemperature); mCmHardwareManager = (CmHardwareManager) activity.getSystemService(Context.CMHW_SERVICE); addPreferencesFromResource(R.xml.livedisplay); PreferenceCategory liveDisplayPrefs = (PreferenceCategory) findPreference(KEY_CATEGORY_LIVE_DISPLAY); PreferenceCategory calibrationPrefs = (PreferenceCategory) findPreference(KEY_CATEGORY_CALIBRATION); int displayMode = Settings.System.getIntForUser( resolver, Settings.System.DISPLAY_TEMPERATURE_MODE, 0, UserHandle.USER_CURRENT); mLiveDisplay = (ListPreference) findPreference(KEY_LIVE_DISPLAY); mLiveDisplay.setValue(String.valueOf(displayMode)); mModeEntries = res.getStringArray(com.android.internal.R.array.live_display_entries); mModeValues = res.getStringArray(com.android.internal.R.array.live_display_values); mModeSummaries = res.getStringArray(com.android.internal.R.array.live_display_summaries); // Remove outdoor mode from lists if there is no support if (!mCmHardwareManager.isSupported(FEATURE_SUNLIGHT_ENHANCEMENT)) { int idx = ArrayUtils.indexOf(mModeValues, String.valueOf(MODE_OUTDOOR)); String[] entriesTemp = new String[mModeEntries.length - 1]; String[] valuesTemp = new String[mModeValues.length - 1]; String[] summariesTemp = new String[mModeSummaries.length - 1]; int j = 0; for (int i = 0; i < mModeEntries.length; i++) { if (i == idx) { continue; } entriesTemp[j] = mModeEntries[i]; valuesTemp[j] = mModeValues[i]; summariesTemp[j] = mModeSummaries[i]; j++; } mModeEntries = entriesTemp; mModeValues = valuesTemp; mModeSummaries = summariesTemp; } mLiveDisplay.setEntries(mModeEntries); mLiveDisplay.setEntryValues(mModeValues); mLiveDisplay.setOnPreferenceChangeListener(this); mDisplayTemperature = (DisplayTemperature) findPreference(KEY_LIVE_DISPLAY_TEMPERATURE); mLowPower = (SwitchPreference) findPreference(KEY_LIVE_DISPLAY_LOW_POWER); if (liveDisplayPrefs != null && mLowPower != null && !mCmHardwareManager.isSupported(FEATURE_ADAPTIVE_BACKLIGHT)) { liveDisplayPrefs.removePreference(mLowPower); mLowPower = null; } mOutdoorMode = (SwitchPreference) findPreference(KEY_LIVE_DISPLAY_AUTO_OUTDOOR_MODE); if (liveDisplayPrefs != null && mOutdoorMode != null && !mCmHardwareManager.isSupported(FEATURE_SUNLIGHT_ENHANCEMENT)) { liveDisplayPrefs.removePreference(mOutdoorMode); mOutdoorMode = null; } mColorEnhancement = (SwitchPreference) findPreference(KEY_LIVE_DISPLAY_COLOR_ENHANCE); if (liveDisplayPrefs != null && mColorEnhancement != null && !mCmHardwareManager.isSupported(FEATURE_COLOR_ENHANCEMENT)) { liveDisplayPrefs.removePreference(mColorEnhancement); mColorEnhancement = null; } if (calibrationPrefs != null && !mCmHardwareManager.isSupported(FEATURE_DISPLAY_GAMMA_CALIBRATION)) { Preference gammaPref = findPreference(KEY_DISPLAY_GAMMA); if (gammaPref != null) { calibrationPrefs.removePreference(gammaPref); } } mScreenColorSettings = (PreferenceScreen) findPreference(KEY_SCREEN_COLOR_SETTINGS); if (calibrationPrefs != null) { if (!isPostProcessingSupported(getActivity()) && mScreenColorSettings != null) { calibrationPrefs.removePreference(mScreenColorSettings); } else if ("user".equals(Build.TYPE)) { // Remove simple RGB controls if HSIC controls are available Preference displayColor = findPreference(KEY_DISPLAY_COLOR); if (displayColor != null) { calibrationPrefs.removePreference(displayColor); } } } }
@Override protected void engineInit(AlgorithmParameterSpec params, SecureRandom random) throws InvalidAlgorithmParameterException { resetAll(); boolean success = false; try { if ((params == null) || (!(params instanceof KeyGenParameterSpec))) { throw new InvalidAlgorithmParameterException( "Cannot initialize without a " + KeyGenParameterSpec.class.getName() + " parameter"); } KeyGenParameterSpec spec = (KeyGenParameterSpec) params; if (spec.getKeystoreAlias() == null) { throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided"); } mRng = random; mSpec = spec; mKeySizeBits = (spec.getKeySize() != -1) ? spec.getKeySize() : mDefaultKeySizeBits; if (mKeySizeBits <= 0) { throw new InvalidAlgorithmParameterException("Key size must be positive: " + mKeySizeBits); } else if ((mKeySizeBits % 8) != 0) { throw new InvalidAlgorithmParameterException( "Key size in must be a multiple of 8: " + mKeySizeBits); } try { mKeymasterPurposes = KeyProperties.Purpose.allToKeymaster(spec.getPurposes()); mKeymasterPaddings = KeyProperties.EncryptionPadding.allToKeymaster(spec.getEncryptionPaddings()); mKeymasterBlockModes = KeyProperties.BlockMode.allToKeymaster(spec.getBlockModes()); if (((spec.getPurposes() & KeyProperties.PURPOSE_ENCRYPT) != 0) && (spec.isRandomizedEncryptionRequired())) { for (int keymasterBlockMode : mKeymasterBlockModes) { if (!KeymasterUtils.isKeymasterBlockModeIndCpaCompatible(keymasterBlockMode)) { throw new InvalidAlgorithmParameterException( "Randomized encryption (IND-CPA) required but may be violated" + " by block mode: " + KeyProperties.BlockMode.fromKeymaster(keymasterBlockMode) + ". See " + KeyGenParameterSpec.class.getName() + " documentation."); } } } if (spec.isDigestsSpecified()) { // Digest(s) explicitly specified in the spec mKeymasterDigests = KeyProperties.Digest.allToKeymaster(spec.getDigests()); if (mKeymasterDigest != -1) { // Key algorithm implies a digest -- ensure it's specified in the spec as // first digest. if (!com.android.internal.util.ArrayUtils.contains( mKeymasterDigests, mKeymasterDigest)) { throw new InvalidAlgorithmParameterException( "Digests specified in algorithm parameters (" + Arrays.asList(spec.getDigests()) + ") must include " + " the digest " + KeyProperties.Digest.fromKeymaster(mKeymasterDigest) + " implied by key algorithm"); } if (mKeymasterDigests[0] != mKeymasterDigest) { // The first digest is not the one implied by the key algorithm. // Swap the implied digest with the first one. for (int i = 0; i < mKeymasterDigests.length; i++) { if (mKeymasterDigests[i] == mKeymasterDigest) { mKeymasterDigests[i] = mKeymasterDigests[0]; mKeymasterDigests[0] = mKeymasterDigest; break; } } } } } else { // No digest specified in the spec if (mKeymasterDigest != -1) { // Key algorithm implies a digest -- use that digest mKeymasterDigests = new int[] {mKeymasterDigest}; } else { mKeymasterDigests = EmptyArray.INT; } } if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_HMAC) { if (mKeymasterDigests.length == 0) { throw new InvalidAlgorithmParameterException( "At least one digest algorithm must be specified"); } } } catch (IllegalStateException | IllegalArgumentException e) { throw new InvalidAlgorithmParameterException(e); } success = true; } finally { if (!success) { resetAll(); } } }
/** This class contains a metadata of suggestions from the text service */ public final class SuggestionsInfo implements Parcelable { private static final String[] EMPTY = ArrayUtils.emptyArray(String.class); /** * Flag of the attributes of the suggestions that can be obtained by {@link * #getSuggestionsAttributes}: this tells that the requested word was found in the dictionary in * the text service. */ public static final int RESULT_ATTR_IN_THE_DICTIONARY = 0x0001; /** * Flag of the attributes of the suggestions that can be obtained by {@link * #getSuggestionsAttributes}: this tells that the text service thinks the requested word looks * like a typo. */ public static final int RESULT_ATTR_LOOKS_LIKE_TYPO = 0x0002; /** * Flag of the attributes of the suggestions that can be obtained by {@link * #getSuggestionsAttributes}: this tells that the text service thinks the result suggestions * include highly recommended ones. */ public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 0x0004; private final int mSuggestionsAttributes; private final String[] mSuggestions; private final boolean mSuggestionsAvailable; private int mCookie; private int mSequence; /** * Constructor. * * @param suggestionsAttributes from the text service * @param suggestions from the text service */ public SuggestionsInfo(int suggestionsAttributes, String[] suggestions) { this(suggestionsAttributes, suggestions, 0, 0); } /** * Constructor. * * @param suggestionsAttributes from the text service * @param suggestions from the text service * @param cookie the cookie of the input TextInfo * @param sequence the cookie of the input TextInfo */ public SuggestionsInfo( int suggestionsAttributes, String[] suggestions, int cookie, int sequence) { if (suggestions == null) { mSuggestions = EMPTY; mSuggestionsAvailable = false; } else { mSuggestions = suggestions; mSuggestionsAvailable = true; } mSuggestionsAttributes = suggestionsAttributes; mCookie = cookie; mSequence = sequence; } public SuggestionsInfo(Parcel source) { mSuggestionsAttributes = source.readInt(); mSuggestions = source.readStringArray(); mCookie = source.readInt(); mSequence = source.readInt(); mSuggestionsAvailable = source.readInt() == 1; } /** * Used to package this object into a {@link Parcel}. * * @param dest The {@link Parcel} to be written. * @param flags The flags used for parceling. */ @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mSuggestionsAttributes); dest.writeStringArray(mSuggestions); dest.writeInt(mCookie); dest.writeInt(mSequence); dest.writeInt(mSuggestionsAvailable ? 1 : 0); } /** * Set the cookie and the sequence of SuggestionsInfo which are set to TextInfo from a client * application * * @param cookie the cookie of an input TextInfo * @param sequence the cookie of an input TextInfo */ public void setCookieAndSequence(int cookie, int sequence) { mCookie = cookie; mSequence = sequence; } /** @return the cookie which may be set by a client application */ public int getCookie() { return mCookie; } /** @return the sequence which may be set by a client application */ public int getSequence() { return mSequence; } /** * @return the attributes of suggestions. This includes whether the spell checker has the word in * its dictionary or not and whether the spell checker has confident suggestions for the word * or not. */ public int getSuggestionsAttributes() { return mSuggestionsAttributes; } /** * @return the count of the suggestions. If there's no suggestions at all, this method returns -1. * Even if this method returns 0, it doesn't necessarily mean that there are no suggestions * for the requested word. For instance, the caller could have been asked to limit the maximum * number of suggestions returned. */ public int getSuggestionsCount() { if (!mSuggestionsAvailable) { return -1; } return mSuggestions.length; } /** * @param i the id of suggestions * @return the suggestion at the specified id */ public String getSuggestionAt(int i) { return mSuggestions[i]; } /** Used to make this class parcelable. */ public static final Creator<SuggestionsInfo> CREATOR = new Creator<SuggestionsInfo>() { @Override public SuggestionsInfo createFromParcel(Parcel source) { return new SuggestionsInfo(source); } @Override public SuggestionsInfo[] newArray(int size) { return new SuggestionsInfo[size]; } }; /** Used to make this class parcelable. */ @Override public int describeContents() { return 0; } }
public void dump(Printer pw, String prefix) { super.dumpFront(pw, prefix); if (className != null) { pw.println(prefix + "className=" + className); } if (permission != null) { pw.println(prefix + "permission=" + permission); } pw.println(prefix + "processName=" + processName); pw.println(prefix + "taskAffinity=" + taskAffinity); pw.println( prefix + "uid=" + uid + " flags=0x" + Integer.toHexString(flags) + " theme=0x" + Integer.toHexString(theme)); pw.println( prefix + "requiresSmallestWidthDp=" + requiresSmallestWidthDp + " compatibleWidthLimitDp=" + compatibleWidthLimitDp + " largestWidthLimitDp=" + largestWidthLimitDp); pw.println(prefix + "sourceDir=" + sourceDir); if (!Objects.equals(sourceDir, publicSourceDir)) { pw.println(prefix + "publicSourceDir=" + publicSourceDir); } if (!ArrayUtils.isEmpty(splitSourceDirs)) { pw.println(prefix + "splitSourceDirs=" + Arrays.toString(splitSourceDirs)); } if (!ArrayUtils.isEmpty(splitPublicSourceDirs) && !Arrays.equals(splitSourceDirs, splitPublicSourceDirs)) { pw.println(prefix + "splitPublicSourceDirs=" + Arrays.toString(splitPublicSourceDirs)); } if (resourceDirs != null) { pw.println(prefix + "resourceDirs=" + resourceDirs); } if (seinfo != null) { pw.println(prefix + "seinfo=" + seinfo); } pw.println(prefix + "dataDir=" + dataDir); if (sharedLibraryFiles != null) { pw.println(prefix + "sharedLibraryFiles=" + sharedLibraryFiles); } pw.println( prefix + "enabled=" + enabled + " targetSdkVersion=" + targetSdkVersion + " versionCode=" + versionCode); if (manageSpaceActivityName != null) { pw.println(prefix + "manageSpaceActivityName=" + manageSpaceActivityName); } if (descriptionRes != 0) { pw.println(prefix + "description=0x" + Integer.toHexString(descriptionRes)); } if (uiOptions != 0) { pw.println(prefix + "uiOptions=0x" + Integer.toHexString(uiOptions)); } pw.println(prefix + "supportsRtl=" + (hasRtlSupport() ? "true" : "false")); super.dumpBack(pw, prefix); }
public void addComponentFilter(ComponentName componentName, Rule rule) { Rule[] rules = mRulesByComponent.get(componentName); rules = ArrayUtils.appendElement(Rule.class, rules, rule); mRulesByComponent.put(componentName, rules); }
public boolean exists(int userId) { synchronized (mPackagesLock) { return ArrayUtils.contains(mUserIds, userId); } }
/** * Test if given {@link Signature} sets are exactly equal. * * @hide */ public static boolean areExactMatch(Signature[] a, Signature[] b) { return (a.length == b.length) && ArrayUtils.containsAll(a, b) && ArrayUtils.containsAll(b, a); }