diff options
Diffstat (limited to 'core/java/com')
50 files changed, 1792 insertions, 719 deletions
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java index 0183e45..20d209f 100644 --- a/core/java/com/android/internal/app/AlertController.java +++ b/core/java/com/android/internal/app/AlertController.java @@ -26,7 +26,6 @@ import android.content.DialogInterface; import android.content.res.TypedArray; import android.database.Cursor; import android.graphics.drawable.Drawable; -import android.os.Build; import android.os.Handler; import android.os.Message; import android.text.TextUtils; @@ -38,9 +37,12 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; +import android.view.ViewParent; +import android.view.ViewTreeObserver; import android.view.Window; import android.view.WindowInsets; import android.view.WindowManager; +import android.widget.AbsListView; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; @@ -449,11 +451,11 @@ public class AlertController { } private void setupView() { - final LinearLayout contentPanel = (LinearLayout) mWindow.findViewById(R.id.contentPanel); + final ViewGroup contentPanel = (ViewGroup) mWindow.findViewById(R.id.contentPanel); setupContent(contentPanel); final boolean hasButtons = setupButtons(); - final LinearLayout topPanel = (LinearLayout) mWindow.findViewById(R.id.topPanel); + final ViewGroup topPanel = (ViewGroup) mWindow.findViewById(R.id.topPanel); final TypedArray a = mContext.obtainStyledAttributes( null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0); final boolean hasTitle = setupTitle(topPanel); @@ -521,13 +523,13 @@ public class AlertController { a.recycle(); } - private boolean setupTitle(LinearLayout topPanel) { + private boolean setupTitle(ViewGroup topPanel) { boolean hasTitle = true; if (mCustomTitleView != null) { // Add the custom title view directly to the topPanel layout - LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT); + LayoutParams lp = new LayoutParams( + LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); topPanel.addView(mCustomTitleView, 0, lp); @@ -571,7 +573,7 @@ public class AlertController { return hasTitle; } - private void setupContent(LinearLayout contentPanel) { + private void setupContent(ViewGroup contentPanel) { mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView); mScrollView.setFocusable(false); @@ -588,14 +590,77 @@ public class AlertController { mScrollView.removeView(mMessageView); if (mListView != null) { - contentPanel.removeView(mWindow.findViewById(R.id.scrollView)); - contentPanel.addView(mListView, - new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); - contentPanel.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1.0f)); + final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent(); + final int childIndex = scrollParent.indexOfChild(mScrollView); + scrollParent.removeViewAt(childIndex); + scrollParent.addView(mListView, childIndex, + new LayoutParams(MATCH_PARENT, MATCH_PARENT)); } else { contentPanel.setVisibility(View.GONE); } } + + // Set up scroll indicators (if present). + final View indicatorUp = mWindow.findViewById(R.id.scrollIndicatorUp); + final View indicatorDown = mWindow.findViewById(R.id.scrollIndicatorDown); + if (indicatorUp != null || indicatorDown != null) { + if (mMessage != null) { + // We're just showing the ScrollView, set up listener. + mScrollView.setOnScrollChangeListener(new View.OnScrollChangeListener() { + @Override + public void onScrollChange(View v, int scrollX, int scrollY, + int oldScrollX, int oldScrollY) { + manageScrollIndicators(v, indicatorUp, indicatorDown); + } + }); + // Set up the indicators following layout. + mScrollView.post(new Runnable() { + @Override + public void run() { + manageScrollIndicators(mScrollView, indicatorUp, indicatorDown); + } + }); + + } else if (mListView != null) { + // We're just showing the AbsListView, set up listener. + mListView.setOnScrollListener(new AbsListView.OnScrollListener() { + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + // That's cool, I guess? + } + + @Override + public void onScroll(AbsListView v, int firstVisibleItem, + int visibleItemCount, int totalItemCount) { + manageScrollIndicators(v, indicatorUp, indicatorDown); + } + }); + // Set up the indicators following layout. + mListView.post(new Runnable() { + @Override + public void run() { + manageScrollIndicators(mListView, indicatorUp, indicatorDown); + } + }); + } else { + // We don't have any content to scroll, remove the indicators. + if (indicatorUp != null) { + contentPanel.removeView(indicatorUp); + } + if (indicatorDown != null) { + contentPanel.removeView(indicatorDown); + } + } + } + } + + private static void manageScrollIndicators(View v, View upIndicator, View downIndicator) { + if (upIndicator != null) { + upIndicator.setVisibility(v.canScrollVertically(-1) ? View.VISIBLE : View.INVISIBLE); + } + if (downIndicator != null) { + downIndicator.setVisibility(v.canScrollVertically(1) ? View.VISIBLE : View.INVISIBLE); + } } private boolean setupButtons() { @@ -890,10 +955,10 @@ public class AlertController { if (mIcon != null) { dialog.setIcon(mIcon); } - if (mIconId >= 0) { + if (mIconId != 0) { dialog.setIcon(mIconId); } - if (mIconAttrId > 0) { + if (mIconAttrId != 0) { dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId)); } } diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java index 5267811..64bd6b6 100644 --- a/core/java/com/android/internal/app/ChooserActivity.java +++ b/core/java/com/android/internal/app/ChooserActivity.java @@ -16,13 +16,21 @@ package com.android.internal.app; +import android.app.Activity; +import android.content.ComponentName; import android.content.Intent; +import android.content.IntentSender; +import android.content.pm.ActivityInfo; import android.os.Bundle; import android.os.Parcelable; import android.util.Log; +import android.util.Slog; public class ChooserActivity extends ResolverActivity { + private static final String TAG = "ChooserActivity"; + private Bundle mReplacementExtras; + private IntentSender mChosenComponentSender; @Override protected void onCreate(Bundle savedInstanceState) { @@ -60,21 +68,45 @@ public class ChooserActivity extends ResolverActivity { initialIntents[i] = in; } } + mChosenComponentSender = intent.getParcelableExtra( + Intent.EXTRA_CHOSEN_COMPONENT_INTENT_SENDER); setSafeForwardingMode(true); super.onCreate(savedInstanceState, target, title, defaultTitleRes, initialIntents, null, false); } - public Intent getReplacementIntent(String packageName, Intent defIntent) { + @Override + public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { + Intent result = defIntent; if (mReplacementExtras != null) { - final Bundle replExtras = mReplacementExtras.getBundle(packageName); + final Bundle replExtras = mReplacementExtras.getBundle(aInfo.packageName); if (replExtras != null) { - final Intent result = new Intent(defIntent); + result = new Intent(defIntent); result.putExtras(replExtras); - return result; } } - return defIntent; + if (aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_USER_OWNER) + || aInfo.name.equals(IntentForwarderActivity.FORWARD_INTENT_TO_MANAGED_PROFILE)) { + result = Intent.createChooser(result, + getIntent().getCharSequenceExtra(Intent.EXTRA_TITLE)); + } + return result; + } + + @Override + public void onActivityStarted(Intent intent) { + if (mChosenComponentSender != null) { + final ComponentName target = intent.getComponent(); + if (target != null) { + final Intent fillIn = new Intent().putExtra(Intent.EXTRA_CHOSEN_COMPONENT, target); + try { + mChosenComponentSender.sendIntent(this, Activity.RESULT_OK, fillIn, null, null); + } catch (IntentSender.SendIntentException e) { + Slog.e(TAG, "Unable to launch supplied IntentSender to report " + + "the chosen component: " + e); + } + } + } } private void modifyTargetIntent(Intent in) { diff --git a/core/java/com/android/internal/app/IAppOpsService.aidl b/core/java/com/android/internal/app/IAppOpsService.aidl index a52dd48..99bf9f3 100644 --- a/core/java/com/android/internal/app/IAppOpsService.aidl +++ b/core/java/com/android/internal/app/IAppOpsService.aidl @@ -36,7 +36,7 @@ interface IAppOpsService { List<AppOpsManager.PackageOps> getPackagesForOps(in int[] ops); List<AppOpsManager.PackageOps> getOpsForPackage(int uid, String packageName, in int[] ops); void setMode(int code, int uid, String packageName, int mode); - void resetAllModes(); + void resetAllModes(int reqUserId, String reqPackageName); int checkAudioOperation(int code, int usage, int uid, String packageName); void setAudioRestriction(int code, int usage, int uid, int mode, in String[] exceptionPackages); diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 55b3ecc..87b6ed7 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -77,6 +77,7 @@ interface IBatteryStats { void noteScreenBrightness(int brightness); void noteUserActivity(int uid, int event); void noteInteractive(boolean interactive); + void noteConnectivityChanged(int type, String extra); void noteMobileRadioPowerState(int powerState, long timestampNs); void notePhoneOn(); void notePhoneOff(); diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java index 6e2f84a..9656a21 100644 --- a/core/java/com/android/internal/app/IntentForwarderActivity.java +++ b/core/java/com/android/internal/app/IntentForwarderActivity.java @@ -58,21 +58,22 @@ public class IntentForwarderActivity extends Activity { Intent intentReceived = getIntent(); String className = intentReceived.getComponent().getClassName(); - final UserHandle userDest; + final int targetUserId; final int userMessageId; if (className.equals(FORWARD_INTENT_TO_USER_OWNER)) { userMessageId = com.android.internal.R.string.forward_intent_to_owner; - userDest = UserHandle.OWNER; + targetUserId = UserHandle.USER_OWNER; } else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) { userMessageId = com.android.internal.R.string.forward_intent_to_work; - userDest = getManagedProfile(); + targetUserId = getManagedProfile(); } else { Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly"); userMessageId = -1; - userDest = null; + targetUserId = UserHandle.USER_NULL; } - if (userDest == null) { // This covers the case where there is no managed profile. + if (targetUserId == UserHandle.USER_NULL) { + // This covers the case where there is no managed profile. finish(); return; } @@ -83,31 +84,24 @@ public class IntentForwarderActivity extends Activity { newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); int callingUserId = getUserId(); - IPackageManager ipm = AppGlobals.getPackageManager(); - String resolvedType = newIntent.resolveTypeIfNeeded(getContentResolver()); - boolean canForward = false; - Intent selector = newIntent.getSelector(); - if (selector == null) { - selector = newIntent; - } - try { - canForward = ipm.canForwardTo(selector, resolvedType, callingUserId, - userDest.getIdentifier()); - } catch (RemoteException e) { - Slog.e(TAG, "PackageManagerService is dead?"); - } - if (canForward) { - newIntent.setContentUserHint(callingUserId); + + if (canForward(newIntent, targetUserId)) { + if (newIntent.getAction().equals(Intent.ACTION_CHOOSER)) { + Intent innerIntent = (Intent) newIntent.getParcelableExtra(Intent.EXTRA_INTENT); + innerIntent.setContentUserHint(callingUserId); + } else { + newIntent.setContentUserHint(callingUserId); + } final android.content.pm.ResolveInfo ri = getPackageManager().resolveActivityAsUser( - newIntent, MATCH_DEFAULT_ONLY, userDest.getIdentifier()); + newIntent, MATCH_DEFAULT_ONLY, targetUserId); // Only show a disclosure if this is a normal (non-OS) app final boolean shouldShowDisclosure = !UserHandle.isSameApp(ri.activityInfo.applicationInfo.uid, Process.SYSTEM_UID); try { - startActivityAsCaller(newIntent, null, userDest.getIdentifier()); + startActivityAsCaller(newIntent, null, targetUserId); } catch (RuntimeException e) { int launchedFromUid = -1; String launchedFromPackage = "?"; @@ -129,26 +123,55 @@ public class IntentForwarderActivity extends Activity { } } else { Slog.wtf(TAG, "the intent: " + newIntent + "cannot be forwarded from user " - + callingUserId + " to user " + userDest.getIdentifier()); + + callingUserId + " to user " + targetUserId); } finish(); } + boolean canForward(Intent intent, int targetUserId) { + IPackageManager ipm = AppGlobals.getPackageManager(); + if (intent.getAction().equals(Intent.ACTION_CHOOSER)) { + // The EXTRA_INITIAL_INTENTS may not be allowed to be forwarded. + if (intent.hasExtra(Intent.EXTRA_INITIAL_INTENTS)) { + Slog.wtf(TAG, "An chooser intent with extra initial intents cannot be forwarded to" + + " a different user"); + return false; + } + if (intent.hasExtra(Intent.EXTRA_REPLACEMENT_EXTRAS)) { + Slog.wtf(TAG, "A chooser intent with replacement extras cannot be forwarded to a" + + " different user"); + return false; + } + intent = (Intent) intent.getParcelableExtra(Intent.EXTRA_INTENT); + } + String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); + if (intent.getSelector() != null) { + intent = intent.getSelector(); + } + try { + return ipm.canForwardTo(intent, resolvedType, getUserId(), + targetUserId); + } catch (RemoteException e) { + Slog.e(TAG, "PackageManagerService is dead?"); + return false; + } + } + /** - * Returns the managed profile for this device or null if there is no managed - * profile. + * Returns the userId of the managed profile for this device or UserHandle.USER_NULL if there is + * no managed profile. * * TODO: Remove the assumption that there is only one managed profile * on the device. */ - private UserHandle getManagedProfile() { + private int getManagedProfile() { UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); List<UserInfo> relatedUsers = userManager.getProfiles(UserHandle.USER_OWNER); for (UserInfo userInfo : relatedUsers) { - if (userInfo.isManagedProfile()) return new UserHandle(userInfo.id); + if (userInfo.isManagedProfile()) return userInfo.id; } Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE + " has been called, but there is no managed profile"); - return null; + return UserHandle.USER_NULL; } } diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java index e3e5647..70fb510 100644 --- a/core/java/com/android/internal/app/ProcessStats.java +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -1079,7 +1079,9 @@ public final class ProcessStats implements Parcelable { ProcessDataCollection totals = new ProcessDataCollection(screenStates, memStates, procStates); computeProcessData(proc, totals, now); - if (totals.totalTime != 0 || totals.numPss != 0) { + double percentage = (double) totals.totalTime / (double) totalTime * 100; + // We don't print percentages < .01, so just drop those. + if (percentage >= 0.005 || totals.numPss != 0) { if (prefix != null) { pw.print(prefix); } @@ -2470,7 +2472,7 @@ public final class ProcessStats implements Parcelable { totalMem.totalTime, totalPss, totalMem.sysMemSamples); totalPss = printMemoryCategory(pw, " ", "Free ", totalMem.sysMemFreeWeight, totalMem.totalTime, totalPss, totalMem.sysMemSamples); - totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight, + totalPss = printMemoryCategory(pw, " ", "Z-Ram ", totalMem.sysMemZRamWeight, totalMem.totalTime, totalPss, totalMem.sysMemSamples); pw.print(" TOTAL : "); printSizeValue(pw, totalPss); @@ -2746,6 +2748,7 @@ public final class ProcessStats implements Parcelable { pw.print("total"); dumpAdjTimesCheckin(pw, ",", mMemFactorDurations, mMemFactor, mStartTime, now); + pw.println(); if (mSysMemUsageTable != null) { pw.print("sysmemusage"); for (int i=0; i<mSysMemUsageTableSize; i++) { diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java index 0062e2d..661acbe 100644 --- a/core/java/com/android/internal/app/ResolverActivity.java +++ b/core/java/com/android/internal/app/ResolverActivity.java @@ -74,6 +74,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; +import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; + /** * This activity is displayed when the system attempts to start an Intent for * which there is more than one matching activity, allowing the user to decide @@ -92,11 +95,14 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private ListView mListView; private Button mAlwaysButton; private Button mOnceButton; + private View mProfileView; private int mIconDpi; private int mIconSize; private int mMaxColumns; private int mLastSelected = ListView.INVALID_POSITION; private boolean mResolvingHome = false; + private int mProfileSwitchMessageId = -1; + private Intent mIntent; private UsageStatsManager mUsm; private Map<String, UsageStats> mStats; @@ -106,6 +112,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private final PackageMonitor mPackageMonitor = new PackageMonitor() { @Override public void onSomePackagesChanged() { mAdapter.handlePackagesChanged(); + if (mProfileView != null) { + bindProfileView(); + } } }; @@ -197,6 +206,11 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic List<ResolveInfo> rList, boolean alwaysUseOption) { setTheme(R.style.Theme_DeviceDefault_Resolver); super.onCreate(savedInstanceState); + + // Determine whether we should show that intent is forwarded + // from managed profile to owner or other way around. + setProfileSwitchMessageId(intent.getContentUserHint()); + try { mLaunchedFromUid = ActivityManagerNative.getDefault().getLaunchedFromUid( getActivityToken()); @@ -208,7 +222,6 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic final long sinceTime = System.currentTimeMillis() - USAGE_STATS_PERIOD; mStats = mUsm.queryAndAggregateUsageStats(sinceTime, System.currentTimeMillis()); - Log.d(TAG, "sinceTime=" + sinceTime); mMaxColumns = getResources().getInteger(R.integer.config_maxResolverActivityColumns); @@ -219,7 +232,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic mIconDpi = am.getLauncherLargeIconDensity(); mIconSize = am.getLauncherLargeIconSize(); - mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList, + mIntent = new Intent(intent); + mAdapter = new ResolveListAdapter(this, initialIntents, rList, mLaunchedFromUid, alwaysUseOption); final int layoutId; @@ -234,12 +248,14 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } mAlwaysUseOption = alwaysUseOption; - int count = mAdapter.mList.size(); if (mLaunchedFromUid < 0 || UserHandle.isIsolated(mLaunchedFromUid)) { // Gulp! finish(); return; - } else if (count > 1) { + } + + int count = mAdapter.mList.size(); + if (count > 1 || (count == 1 && mAdapter.getOtherProfile() != null)) { setContentView(layoutId); mListView = (ListView) findViewById(R.id.resolver_list); mListView.setAdapter(mAdapter); @@ -269,12 +285,15 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic mListView = (ListView) findViewById(R.id.resolver_list); mListView.setVisibility(View.GONE); } + // Prevent the Resolver window from becoming the top fullscreen window and thus from taking + // control of the system bars. + getWindow().clearFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR); final ResolverDrawerLayout rdl = (ResolverDrawerLayout) findViewById(R.id.contentPanel); if (rdl != null) { - rdl.setOnClickOutsideListener(new View.OnClickListener() { + rdl.setOnDismissedListener(new ResolverDrawerLayout.OnDismissedListener() { @Override - public void onClick(View v) { + public void onDismissed() { finish(); } }); @@ -312,6 +331,56 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic setAlwaysButtonEnabled(true, mAdapter.getFilteredPosition(), false); mOnceButton.setEnabled(true); } + + mProfileView = findViewById(R.id.profile_button); + if (mProfileView != null) { + mProfileView.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + final DisplayResolveInfo dri = mAdapter.getOtherProfile(); + if (dri == null) { + return; + } + + final Intent intent = intentForDisplayResolveInfo(dri); + onIntentSelected(dri.ri, intent, mAlwaysUseOption); + finish(); + } + }); + bindProfileView(); + } + } + + void bindProfileView() { + final DisplayResolveInfo dri = mAdapter.getOtherProfile(); + if (dri != null) { + mProfileView.setVisibility(View.VISIBLE); + final ImageView icon = (ImageView) mProfileView.findViewById(R.id.icon); + final TextView text = (TextView) mProfileView.findViewById(R.id.text1); + if (dri.displayIcon == null) { + new LoadIconTask().execute(dri); + } + icon.setImageDrawable(dri.displayIcon); + text.setText(dri.displayLabel); + } else { + mProfileView.setVisibility(View.GONE); + } + } + + private void setProfileSwitchMessageId(int contentUserHint) { + if (contentUserHint != UserHandle.USER_CURRENT && + contentUserHint != UserHandle.myUserId()) { + UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE); + UserInfo originUserInfo = userManager.getUserInfo(contentUserHint); + boolean originIsManaged = originUserInfo != null ? originUserInfo.isManagedProfile() + : false; + boolean targetIsManaged = userManager.isManagedProfile(); + if (originIsManaged && !targetIsManaged) { + mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_owner; + } else if (!originIsManaged && targetIsManaged) { + mProfileSwitchMessageId = com.android.internal.R.string.forward_intent_to_work; + } + } } /** @@ -388,6 +457,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic mRegistered = true; } mAdapter.handlePackagesChanged(); + if (mProfileView != null) { + bindProfileView(); + } } @Override @@ -523,7 +595,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic /** * Replace me in subclasses! */ - public Intent getReplacementIntent(String packageName, Intent defIntent) { + public Intent getReplacementIntent(ActivityInfo aInfo, Intent defIntent) { return defIntent; } @@ -636,12 +708,19 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } public void safelyStartActivity(Intent intent) { + // If needed, show that intent is forwarded + // from managed profile to owner or other way around. + if (mProfileSwitchMessageId != -1) { + Toast.makeText(this, getString(mProfileSwitchMessageId), Toast.LENGTH_LONG).show(); + } if (!mSafeForwardingMode) { startActivity(intent); + onActivityStarted(intent); return; } try { startActivityAsCaller(intent, null, UserHandle.USER_NULL); + onActivityStarted(intent); } catch (RuntimeException e) { String launchedFromPackage; try { @@ -656,6 +735,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } + public void onActivityStarted(Intent intent) { + // Do nothing + } + void showAppDetails(ResolveInfo ri) { Intent in = new Intent().setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", ri.activityInfo.packageName, null)) @@ -663,6 +746,17 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic startActivity(in); } + Intent intentForDisplayResolveInfo(DisplayResolveInfo dri) { + Intent intent = new Intent(dri.origIntent != null ? dri.origIntent : + getReplacementIntent(dri.ri.activityInfo, mIntent)); + intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT + |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); + ActivityInfo ai = dri.ri.activityInfo; + intent.setComponent(new ComponentName( + ai.applicationInfo.packageName, ai.name)); + return intent; + } + private final class DisplayResolveInfo { ResolveInfo ri; CharSequence displayLabel; @@ -683,7 +777,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private final Intent[] mInitialIntents; private final List<ResolveInfo> mBaseResolveList; private ResolveInfo mLastChosen; - private final Intent mIntent; + private DisplayResolveInfo mOtherProfile; private final int mLaunchedFromUid; private final LayoutInflater mInflater; @@ -693,10 +787,8 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic private int mLastChosenPosition = -1; private boolean mFilterLastUsed; - public ResolveListAdapter(Context context, Intent intent, - Intent[] initialIntents, List<ResolveInfo> rList, int launchedFromUid, - boolean filterLastUsed) { - mIntent = new Intent(intent); + public ResolveListAdapter(Context context, Intent[] initialIntents, + List<ResolveInfo> rList, int launchedFromUid, boolean filterLastUsed) { mInitialIntents = initialIntents; mBaseResolveList = rList; mLaunchedFromUid = launchedFromUid; @@ -707,11 +799,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } public void handlePackagesChanged() { - final int oldItemCount = getCount(); rebuildList(); notifyDataSetChanged(); - final int newItemCount = getCount(); - if (newItemCount == 0) { + if (getCount() == 0) { // We no longer have any items... just finish the activity. finish(); } @@ -725,6 +815,10 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return null; } + public DisplayResolveInfo getOtherProfile() { + return mOtherProfile; + } + public int getFilteredPosition() { if (mFilterLastUsed && mLastChosenPosition >= 0) { return mLastChosenPosition; @@ -801,7 +895,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } if (N > 1) { Comparator<ResolveInfo> rComparator = - new ResolverComparator(ResolverActivity.this); + new ResolverComparator(ResolverActivity.this, mIntent); Collections.sort(currentResolveList, rComparator); } // First put the initial items at the top. @@ -819,6 +913,11 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } ResolveInfo ri = new ResolveInfo(); ri.activityInfo = ai; + UserManager userManager = + (UserManager) getSystemService(Context.USER_SERVICE); + if (userManager.isManagedProfile()) { + ri.noResourceId = true; + } if (ii instanceof LabeledIntent) { LabeledIntent li = (LabeledIntent)ii; ri.resolvePackageName = li.getSourcePackage(); @@ -826,7 +925,7 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic ri.nonLocalizedLabel = li.getNonLocalizedLabel(); ri.icon = li.getIconResource(); } - mList.add(new DisplayResolveInfo(ri, + addResolveInfo(new DisplayResolveInfo(ri, ri.loadLabel(getPackageManager()), null, ii)); } } @@ -857,6 +956,13 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic // Process last group processGroup(currentResolveList, start, (N-1), r0, r0Label); } + + // Layout doesn't handle both profile button and last chosen + // so disable last chosen if profile button is present. + if (mOtherProfile != null && mLastChosenPosition >= 0) { + mLastChosenPosition = -1; + mFilterLastUsed = false; + } } private void processGroup(List<ResolveInfo> rList, int start, int end, ResolveInfo ro, @@ -864,14 +970,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic // Process labels from start to i int num = end - start+1; if (num == 1) { - if (mLastChosen != null - && mLastChosen.activityInfo.packageName.equals( - ro.activityInfo.packageName) - && mLastChosen.activityInfo.name.equals(ro.activityInfo.name)) { - mLastChosenPosition = mList.size(); - } // No duplicate labels. Use label for entry at start - mList.add(new DisplayResolveInfo(ro, roLabel, null, null)); + addResolveInfo(new DisplayResolveInfo(ro, roLabel, null, null)); + updateLastChosenPosition(ro); } else { mShowExtended = true; boolean usePkg = false; @@ -899,40 +1000,45 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } for (int k = start; k <= end; k++) { ResolveInfo add = rList.get(k); - if (mLastChosen != null - && mLastChosen.activityInfo.packageName.equals( - add.activityInfo.packageName) - && mLastChosen.activityInfo.name.equals(add.activityInfo.name)) { - mLastChosenPosition = mList.size(); - } if (usePkg) { // Use application name for all entries from start to end-1 - mList.add(new DisplayResolveInfo(add, roLabel, + addResolveInfo(new DisplayResolveInfo(add, roLabel, add.activityInfo.packageName, null)); } else { // Use package name for all entries from start to end-1 - mList.add(new DisplayResolveInfo(add, roLabel, + addResolveInfo(new DisplayResolveInfo(add, roLabel, add.activityInfo.applicationInfo.loadLabel(mPm), null)); } + updateLastChosenPosition(add); } } } + private void updateLastChosenPosition(ResolveInfo info) { + if (mLastChosen != null + && mLastChosen.activityInfo.packageName.equals(info.activityInfo.packageName) + && mLastChosen.activityInfo.name.equals(info.activityInfo.name)) { + mLastChosenPosition = mList.size() - 1; + } + } + + private void addResolveInfo(DisplayResolveInfo dri) { + if (dri.ri.targetUserId != UserHandle.USER_CURRENT && mOtherProfile == null) { + // So far we only support a single other profile at a time. + // The first one we see gets special treatment. + mOtherProfile = dri; + } else { + mList.add(dri); + } + } + public ResolveInfo resolveInfoForPosition(int position, boolean filtered) { return (filtered ? getItem(position) : mList.get(position)).ri; } public Intent intentForPosition(int position, boolean filtered) { DisplayResolveInfo dri = filtered ? getItem(position) : mList.get(position); - - Intent intent = new Intent(dri.origIntent != null ? dri.origIntent : - getReplacementIntent(dri.ri.activityInfo.packageName, mIntent)); - intent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT - |Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP); - ActivityInfo ai = dri.ri.activityInfo; - intent.setComponent(new ComponentName( - ai.applicationInfo.packageName, ai.name)); - return intent; + return intentForDisplayResolveInfo(dri); } public int getCount() { @@ -1023,6 +1129,9 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic @Override protected void onPostExecute(DisplayResolveInfo info) { + if (mProfileView != null && mAdapter.getOtherProfile() == info) { + bindProfileView(); + } mAdapter.notifyDataSetChanged(); } } @@ -1049,11 +1158,20 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } + static final boolean isSpecificUriMatch(int match) { + match = match&IntentFilter.MATCH_CATEGORY_MASK; + return match >= IntentFilter.MATCH_CATEGORY_HOST + && match <= IntentFilter.MATCH_CATEGORY_PATH; + } + class ResolverComparator implements Comparator<ResolveInfo> { private final Collator mCollator; + private final boolean mHttp; - public ResolverComparator(Context context) { + public ResolverComparator(Context context, Intent intent) { mCollator = Collator.getInstance(context.getResources().getConfiguration().locale); + String scheme = intent.getScheme(); + mHttp = "http".equals(scheme) || "https".equals(scheme); } @Override @@ -1063,6 +1181,17 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic return 1; } + if (mHttp) { + // Special case: we want filters that match URI paths/schemes to be + // ordered before others. This is for the case when opening URIs, + // to make native apps go above browsers. + final boolean lhsSpecific = isSpecificUriMatch(lhs.match); + final boolean rhsSpecific = isSpecificUriMatch(rhs.match); + if (lhsSpecific != rhsSpecific) { + return lhsSpecific ? -1 : 1; + } + } + if (mStats != null) { final long timeDiff = getPackageTimeSpent(rhs.activityInfo.packageName) - @@ -1093,4 +1222,3 @@ public class ResolverActivity extends Activity implements AdapterView.OnItemClic } } } - diff --git a/core/java/com/android/internal/app/ShutdownActivity.java b/core/java/com/android/internal/app/ShutdownActivity.java new file mode 100644 index 0000000..97521cf --- /dev/null +++ b/core/java/com/android/internal/app/ShutdownActivity.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IPowerManager; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.util.Slog; + +public class ShutdownActivity extends Activity { + + private static final String TAG = "ShutdownActivity"; + private boolean mReboot; + private boolean mConfirm; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + Intent intent = getIntent(); + mReboot = Intent.ACTION_REBOOT.equals(intent.getAction()); + mConfirm = intent.getBooleanExtra(Intent.EXTRA_KEY_CONFIRM, false); + Slog.i(TAG, "onCreate(): confirm=" + mConfirm); + + Thread thr = new Thread("ShutdownActivity") { + @Override + public void run() { + IPowerManager pm = IPowerManager.Stub.asInterface( + ServiceManager.getService(Context.POWER_SERVICE)); + try { + if (mReboot) { + pm.reboot(mConfirm, null, false); + } else { + pm.shutdown(mConfirm, false); + } + } catch (RemoteException e) { + } + } + }; + thr.start(); + finish(); + // Wait for us to tell the power manager to shutdown. + try { + thr.join(); + } catch (InterruptedException e) { + } + } +} diff --git a/core/java/com/android/internal/app/ToolbarActionBar.java b/core/java/com/android/internal/app/ToolbarActionBar.java index 4410f25..34b9dcb 100644 --- a/core/java/com/android/internal/app/ToolbarActionBar.java +++ b/core/java/com/android/internal/app/ToolbarActionBar.java @@ -40,7 +40,6 @@ import com.android.internal.widget.ToolbarWidgetWrapper; import java.util.ArrayList; public class ToolbarActionBar extends ActionBar { - private Toolbar mToolbar; private DecorToolbar mDecorToolbar; private boolean mToolbarMenuPrepared; private Window.Callback mWindowCallback; @@ -66,7 +65,6 @@ public class ToolbarActionBar extends ActionBar { }; public ToolbarActionBar(Toolbar toolbar, CharSequence title, Window.Callback windowCallback) { - mToolbar = toolbar; mDecorToolbar = new ToolbarWidgetWrapper(toolbar, false); mWindowCallback = new ToolbarCallbackWrapper(windowCallback); mDecorToolbar.setWindowCallback(mWindowCallback); @@ -91,8 +89,8 @@ public class ToolbarActionBar extends ActionBar { @Override public void setCustomView(int resId) { - final LayoutInflater inflater = LayoutInflater.from(mToolbar.getContext()); - setCustomView(inflater.inflate(resId, mToolbar, false)); + final LayoutInflater inflater = LayoutInflater.from(mDecorToolbar.getContext()); + setCustomView(inflater.inflate(resId, mDecorToolbar.getViewGroup(), false)); } @Override @@ -132,17 +130,17 @@ public class ToolbarActionBar extends ActionBar { @Override public void setElevation(float elevation) { - mToolbar.setElevation(elevation); + mDecorToolbar.getViewGroup().setElevation(elevation); } @Override public float getElevation() { - return mToolbar.getElevation(); + return mDecorToolbar.getViewGroup().getElevation(); } @Override public Context getThemedContext() { - return mToolbar.getContext(); + return mDecorToolbar.getContext(); } @Override @@ -152,12 +150,12 @@ public class ToolbarActionBar extends ActionBar { @Override public void setHomeAsUpIndicator(Drawable indicator) { - mToolbar.setNavigationIcon(indicator); + mDecorToolbar.setNavigationIcon(indicator); } @Override public void setHomeAsUpIndicator(int resId) { - mToolbar.setNavigationIcon(resId); + mDecorToolbar.setNavigationIcon(resId); } @Override @@ -280,7 +278,7 @@ public class ToolbarActionBar extends ActionBar { @Override public void setBackgroundDrawable(@Nullable Drawable d) { - mToolbar.setBackground(d); + mDecorToolbar.setBackgroundDrawable(d); } @Override @@ -290,12 +288,12 @@ public class ToolbarActionBar extends ActionBar { @Override public CharSequence getTitle() { - return mToolbar.getTitle(); + return mDecorToolbar.getTitle(); } @Override public CharSequence getSubtitle() { - return mToolbar.getSubtitle(); + return mDecorToolbar.getSubtitle(); } @Override @@ -389,44 +387,44 @@ public class ToolbarActionBar extends ActionBar { @Override public int getHeight() { - return mToolbar.getHeight(); + return mDecorToolbar.getHeight(); } @Override public void show() { // TODO: Consider a better transition for this. // Right now use no automatic transition so that the app can supply one if desired. - mToolbar.setVisibility(View.VISIBLE); + mDecorToolbar.setVisibility(View.VISIBLE); } @Override public void hide() { // TODO: Consider a better transition for this. // Right now use no automatic transition so that the app can supply one if desired. - mToolbar.setVisibility(View.GONE); + mDecorToolbar.setVisibility(View.GONE); } @Override public boolean isShowing() { - return mToolbar.getVisibility() == View.VISIBLE; + return mDecorToolbar.getVisibility() == View.VISIBLE; } @Override public boolean openOptionsMenu() { - return mToolbar.showOverflowMenu(); + return mDecorToolbar.showOverflowMenu(); } @Override public boolean invalidateOptionsMenu() { - mToolbar.removeCallbacks(mMenuInvalidator); - mToolbar.postOnAnimation(mMenuInvalidator); + mDecorToolbar.getViewGroup().removeCallbacks(mMenuInvalidator); + mDecorToolbar.getViewGroup().postOnAnimation(mMenuInvalidator); return true; } @Override public boolean collapseActionView() { - if (mToolbar.hasExpandedActionView()) { - mToolbar.collapseActionView(); + if (mDecorToolbar.hasExpandedActionView()) { + mDecorToolbar.collapseActionView(); return true; } return false; @@ -434,10 +432,10 @@ public class ToolbarActionBar extends ActionBar { void populateOptionsMenu() { if (!mMenuCallbackSet) { - mToolbar.setMenuCallbacks(new ActionMenuPresenterCallback(), new MenuBuilderCallback()); + mDecorToolbar.setMenuCallbacks(new ActionMenuPresenterCallback(), new MenuBuilderCallback()); mMenuCallbackSet = true; } - final Menu menu = mToolbar.getMenu(); + final Menu menu = mDecorToolbar.getMenu(); final MenuBuilder mb = menu instanceof MenuBuilder ? (MenuBuilder) menu : null; if (mb != null) { mb.stopDispatchingItemsChanged(); @@ -518,7 +516,7 @@ public class ToolbarActionBar extends ActionBar { } mClosingActionMenu = true; - mToolbar.dismissPopupMenus(); + mDecorToolbar.dismissPopupMenus(); if (mWindowCallback != null) { mWindowCallback.onPanelClosed(Window.FEATURE_ACTION_BAR, menu); } @@ -536,7 +534,7 @@ public class ToolbarActionBar extends ActionBar { @Override public void onMenuModeChange(MenuBuilder menu) { if (mWindowCallback != null) { - if (mToolbar.isOverflowMenuShowing()) { + if (mDecorToolbar.isOverflowMenuShowing()) { mWindowCallback.onPanelClosed(Window.FEATURE_ACTION_BAR, menu); } else if (mWindowCallback.onPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) { diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java index 2377c22..061b535 100644 --- a/core/java/com/android/internal/app/WindowDecorActionBar.java +++ b/core/java/com/android/internal/app/WindowDecorActionBar.java @@ -496,7 +496,7 @@ public class WindowDecorActionBar extends ActionBar implements mOverlayLayout.setHideOnContentScrollEnabled(false); mContextView.killMode(); - ActionModeImpl mode = new ActionModeImpl(callback); + ActionModeImpl mode = new ActionModeImpl(mContextView.getContext(), callback); if (mode.dispatchOnCreate()) { mode.invalidate(); mContextView.initForMode(mode); @@ -876,7 +876,7 @@ public class WindowDecorActionBar extends ActionBar implements currentTheme.resolveAttribute(com.android.internal.R.attr.actionBarWidgetTheme, outValue, true); final int targetThemeRes = outValue.resourceId; - + if (targetThemeRes != 0 && mContext.getThemeResId() != targetThemeRes) { mThemedContext = new ContextThemeWrapper(mContext, targetThemeRes); } else { @@ -885,7 +885,7 @@ public class WindowDecorActionBar extends ActionBar implements } return mThemedContext; } - + @Override public boolean isTitleTruncated() { return mDecorToolbar != null && mDecorToolbar.isTitleTruncated(); @@ -933,23 +933,26 @@ public class WindowDecorActionBar extends ActionBar implements } /** - * @hide + * @hide */ public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback { + private final Context mActionModeContext; + private final MenuBuilder mMenu; + private ActionMode.Callback mCallback; - private MenuBuilder mMenu; private WeakReference<View> mCustomView; - - public ActionModeImpl(ActionMode.Callback callback) { + + public ActionModeImpl(Context context, ActionMode.Callback callback) { + mActionModeContext = context; mCallback = callback; - mMenu = new MenuBuilder(getThemedContext()) + mMenu = new MenuBuilder(context) .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); mMenu.setCallback(this); } @Override public MenuInflater getMenuInflater() { - return new MenuInflater(getThemedContext()); + return new MenuInflater(mActionModeContext); } @Override @@ -990,6 +993,13 @@ public class WindowDecorActionBar extends ActionBar implements @Override public void invalidate() { + if (mActionMode != this) { + // Not the active action mode - no-op. It's possible we are + // currently deferring onDestroy, so the app doesn't yet know we + // are going away and is trying to use us. That's also a no-op. + return; + } + mMenu.stopDispatchingItemsChanged(); try { mCallback.onPrepareActionMode(this, mMenu); @@ -1042,7 +1052,7 @@ public class WindowDecorActionBar extends ActionBar implements public CharSequence getSubtitle() { return mContextView.getSubtitle(); } - + @Override public void setTitleOptionalHint(boolean titleOptional) { super.setTitleOptionalHint(titleOptional); diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index d8dffe0..044383e 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -35,6 +35,8 @@ import android.util.Log; import com.android.org.bouncycastle.util.encoders.Base64; +import libcore.io.IoUtils; + import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; @@ -559,11 +561,7 @@ public class LocalTransport extends BackupTransport { // Full restore handling private void resetFullRestoreState() { - try { - mCurFullRestoreStream.close(); - } catch (IOException e) { - Log.w(TAG, "Unable to close full restore input stream"); - } + IoUtils.closeQuietly(mCurFullRestoreStream); mCurFullRestoreStream = null; mFullRestoreSocketStream = null; mFullRestoreBuffer = null; diff --git a/core/java/com/android/internal/content/PackageMonitor.java b/core/java/com/android/internal/content/PackageMonitor.java index 9df8ad5..eff44bd 100644 --- a/core/java/com/android/internal/content/PackageMonitor.java +++ b/core/java/com/android/internal/content/PackageMonitor.java @@ -24,6 +24,7 @@ import android.net.Uri; import android.os.Handler; import android.os.Looper; import android.os.UserHandle; +import android.util.Slog; import com.android.internal.os.BackgroundThread; import java.util.HashSet; @@ -279,8 +280,8 @@ public abstract class PackageMonitor extends android.content.BroadcastReceiver { mChangeUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); if (mChangeUserId == UserHandle.USER_NULL) { - throw new IllegalArgumentException( - "Intent broadcast does not contain user handle: " + intent); + Slog.w("PackageMonitor", "Intent broadcast does not contain user handle: " + intent); + return; } onBeginPackageChanges(); diff --git a/core/java/com/android/internal/content/ReferrerIntent.aidl b/core/java/com/android/internal/content/ReferrerIntent.aidl new file mode 100644 index 0000000..7cf6774 --- /dev/null +++ b/core/java/com/android/internal/content/ReferrerIntent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.content; + +parcelable ReferrerIntent; diff --git a/core/java/com/android/internal/content/ReferrerIntent.java b/core/java/com/android/internal/content/ReferrerIntent.java new file mode 100644 index 0000000..8d9a1cf --- /dev/null +++ b/core/java/com/android/internal/content/ReferrerIntent.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.content; + +import android.content.Intent; +import android.os.Parcel; + +/** + * Subclass of Intent that also contains referrer (as a package name) information. + */ +public class ReferrerIntent extends Intent { + public final String mReferrer; + + public ReferrerIntent(Intent baseIntent, String referrer) { + super(baseIntent); + mReferrer = referrer; + } + + public void writeToParcel(Parcel dest, int parcelableFlags) { + super.writeToParcel(dest, parcelableFlags); + dest.writeString(mReferrer); + } + + ReferrerIntent(Parcel in) { + readFromParcel(in); + mReferrer = in.readString(); + } + + public static final Creator<ReferrerIntent> CREATOR = new Creator<ReferrerIntent>() { + public ReferrerIntent createFromParcel(Parcel source) { + return new ReferrerIntent(source); + } + public ReferrerIntent[] newArray(int size) { + return new ReferrerIntent[size]; + } + }; +} diff --git a/core/java/com/android/internal/http/multipart/FilePart.java b/core/java/com/android/internal/http/multipart/FilePart.java index bfcda00..45e4be6 100644 --- a/core/java/com/android/internal/http/multipart/FilePart.java +++ b/core/java/com/android/internal/http/multipart/FilePart.java @@ -51,9 +51,14 @@ import org.apache.commons.logging.LogFactory; * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a> * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a> * - * @since 2.0 + * @since 2.0 * + * @deprecated Please use {@link java.net.URLConnection} and friends instead. + * The Apache HTTP client is no longer maintained and may be removed in a future + * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. */ +@Deprecated public class FilePart extends PartBase { /** Default content encoding of file attachments. */ diff --git a/core/java/com/android/internal/http/multipart/MultipartEntity.java b/core/java/com/android/internal/http/multipart/MultipartEntity.java index 2c5e7f6..5319251 100644 --- a/core/java/com/android/internal/http/multipart/MultipartEntity.java +++ b/core/java/com/android/internal/http/multipart/MultipartEntity.java @@ -80,7 +80,13 @@ import org.apache.commons.logging.LogFactory; * </pre> * * @since 3.0 + * + * @deprecated Please use {@link java.net.URLConnection} and friends instead. + * The Apache HTTP client is no longer maintained and may be removed in a future + * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. */ +@Deprecated public class MultipartEntity extends AbstractHttpEntity { private static final Log log = LogFactory.getLog(MultipartEntity.class); diff --git a/core/java/com/android/internal/http/multipart/Part.java b/core/java/com/android/internal/http/multipart/Part.java index cb1b546..1d66dc6 100644 --- a/core/java/com/android/internal/http/multipart/Part.java +++ b/core/java/com/android/internal/http/multipart/Part.java @@ -48,7 +48,13 @@ import org.apache.commons.logging.LogFactory; * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a> * * @since 2.0 + * + * @deprecated Please use {@link java.net.URLConnection} and friends instead. + * The Apache HTTP client is no longer maintained and may be removed in a future + * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. */ +@Deprecated public abstract class Part { /** Log object for this class. */ diff --git a/core/java/com/android/internal/http/multipart/StringPart.java b/core/java/com/android/internal/http/multipart/StringPart.java index c98257e..73d0f90 100644 --- a/core/java/com/android/internal/http/multipart/StringPart.java +++ b/core/java/com/android/internal/http/multipart/StringPart.java @@ -46,7 +46,13 @@ import org.apache.commons.logging.LogFactory; * @author <a href="mailto:oleg@ural.ru">Oleg Kalnichevski</a> * * @since 2.0 + * + * @deprecated Please use {@link java.net.URLConnection} and friends instead. + * The Apache HTTP client is no longer maintained and may be removed in a future + * release. Please visit <a href="http://android-developers.blogspot.com/2011/09/androids-http-clients.html">this webpage</a> + * for further details. */ +@Deprecated public class StringPart extends PartBase { /** Log object for this class. */ diff --git a/core/java/com/android/internal/inputmethod/InputMethodUtils.java b/core/java/com/android/internal/inputmethod/InputMethodUtils.java index ac915d1..183527c 100644 --- a/core/java/com/android/internal/inputmethod/InputMethodUtils.java +++ b/core/java/com/android/internal/inputmethod/InputMethodUtils.java @@ -16,6 +16,8 @@ package com.android.internal.inputmethod; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.ContentResolver; import android.content.Context; @@ -34,7 +36,9 @@ import android.view.textservice.SpellCheckerInfo; import android.view.textservice.TextServicesManager; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; @@ -115,8 +119,8 @@ public class InputMethodUtils { } /** - * @deprecated Use {@link Locale} returned from - * {@link #getFallbackLocaleForDefaultIme(ArrayList)} instead. + * @deprecated Use {@link #isSystemImeThatHasSubtypeOf(InputMethodInfo, Context, boolean, + * Locale, boolean, String)} instead. */ @Deprecated public static boolean isSystemImeThatHasEnglishKeyboardSubtype(InputMethodInfo imi) { @@ -126,25 +130,60 @@ public class InputMethodUtils { return containsSubtypeOf(imi, ENGLISH_LOCALE.getLanguage(), SUBTYPE_MODE_KEYBOARD); } + private static boolean isSystemImeThatHasSubtypeOf(final InputMethodInfo imi, + final Context context, final boolean checkDefaultAttribute, + @Nullable final Locale requiredLocale, final boolean checkCountry, + final String requiredSubtypeMode) { + if (!isSystemIme(imi)) { + return false; + } + if (checkDefaultAttribute && !imi.isDefault(context)) { + return false; + } + if (!containsSubtypeOf(imi, requiredLocale, checkCountry, requiredSubtypeMode)) { + return false; + } + return true; + } + + @Nullable public static Locale getFallbackLocaleForDefaultIme(final ArrayList<InputMethodInfo> imis, final Context context) { + // At first, find the fallback locale from the IMEs that are declared as "default" in the + // current locale. Note that IME developers can declare an IME as "default" only for + // some particular locales but "not default" for other locales. for (final Locale fallbackLocale : SEARCH_ORDER_OF_FALLBACK_LOCALES) { for (int i = 0; i < imis.size(); ++i) { - final InputMethodInfo imi = imis.get(i); - if (isSystemIme(imi) && imi.isDefault(context) && - containsSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */, - SUBTYPE_MODE_KEYBOARD)) { + if (isSystemImeThatHasSubtypeOf(imis.get(i), context, + true /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD)) { + return fallbackLocale; + } + } + } + // If no fallback locale is found in the above condition, find fallback locales regardless + // of the "default" attribute as a last resort. + for (final Locale fallbackLocale : SEARCH_ORDER_OF_FALLBACK_LOCALES) { + for (int i = 0; i < imis.size(); ++i) { + if (isSystemImeThatHasSubtypeOf(imis.get(i), context, + false /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD)) { return fallbackLocale; } } } + Slog.w(TAG, "Found no fallback locale. imis=" + Arrays.toString(imis.toArray())); return null; } - private static boolean isSystemAuxilialyImeThatHasAutomaticSubtype(InputMethodInfo imi) { + private static boolean isSystemAuxilialyImeThatHasAutomaticSubtype(final InputMethodInfo imi, + final Context context, final boolean checkDefaultAttribute) { if (!isSystemIme(imi)) { return false; } + if (checkDefaultAttribute && !imi.isDefault(context)) { + return false; + } if (!imi.isAuxiliaryIme()) { return false; } @@ -166,98 +205,184 @@ public class InputMethodUtils { } } - public static ArrayList<InputMethodInfo> getDefaultEnabledImes( - Context context, boolean isSystemReady, ArrayList<InputMethodInfo> imis) { - // OK to store null in fallbackLocale because isImeThatHasSubtypeOf() is null-tolerant. - final Locale fallbackLocale = getFallbackLocaleForDefaultIme(imis, context); + private static final class InputMethodListBuilder { + // Note: We use LinkedHashSet instead of android.util.ArraySet because the enumeration + // order can have non-trivial effect in the call sites. + @NonNull + private final LinkedHashSet<InputMethodInfo> mInputMethodSet = new LinkedHashSet<>(); - if (!isSystemReady) { - final ArrayList<InputMethodInfo> retval = new ArrayList<>(); + public InputMethodListBuilder fillImes(final ArrayList<InputMethodInfo> imis, + final Context context, final boolean checkDefaultAttribute, + @Nullable final Locale locale, final boolean checkCountry, + final String requiredSubtypeMode) { for (int i = 0; i < imis.size(); ++i) { final InputMethodInfo imi = imis.get(i); - // TODO: We should check isAsciiCapable instead of relying on fallbackLocale. - if (isSystemIme(imi) && imi.isDefault(context) && - isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */, - SUBTYPE_MODE_KEYBOARD)) { - retval.add(imi); + if (isSystemImeThatHasSubtypeOf(imi, context, checkDefaultAttribute, locale, + checkCountry, requiredSubtypeMode)) { + mInputMethodSet.add(imi); } } - return retval; + return this; } - // OK to store null in fallbackLocale because isImeThatHasSubtypeOf() is null-tolerant. - final Locale systemLocale = getSystemLocaleFromContext(context); - // TODO: Use LinkedHashSet to simplify the code. - final ArrayList<InputMethodInfo> retval = new ArrayList<>(); - boolean systemLocaleKeyboardImeFound = false; - - // First, try to find IMEs with taking the system locale country into consideration. - for (int i = 0; i < imis.size(); ++i) { - final InputMethodInfo imi = imis.get(i); - if (!isSystemIme(imi) || !imi.isDefault(context)) { - continue; - } - final boolean isSystemLocaleKeyboardIme = isImeThatHasSubtypeOf(imi, systemLocale, - false /* ignoreCountry */, SUBTYPE_MODE_KEYBOARD); - // TODO: We should check isAsciiCapable instead of relying on fallbackLocale. - // TODO: Use LinkedHashSet to simplify the code. - if (isSystemLocaleKeyboardIme || - isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */, - SUBTYPE_MODE_ANY)) { - retval.add(imi); + // TODO: The behavior of InputMethodSubtype#overridesImplicitlyEnabledSubtype() should be + // documented more clearly. + public InputMethodListBuilder fillAuxiliaryImes(final ArrayList<InputMethodInfo> imis, + final Context context) { + // If one or more auxiliary input methods are available, OK to stop populating the list. + for (final InputMethodInfo imi : mInputMethodSet) { + if (imi.isAuxiliaryIme()) { + return this; + } } - systemLocaleKeyboardImeFound |= isSystemLocaleKeyboardIme; - } - - // System locale country doesn't match any IMEs, try to find IMEs in a country-agnostic - // way. - if (!systemLocaleKeyboardImeFound) { + boolean added = false; for (int i = 0; i < imis.size(); ++i) { final InputMethodInfo imi = imis.get(i); - if (!isSystemIme(imi) || !imi.isDefault(context)) { - continue; - } - if (isImeThatHasSubtypeOf(imi, fallbackLocale, false /* ignoreCountry */, - SUBTYPE_MODE_KEYBOARD)) { - // IMEs that have fallback locale are already added in the previous loop. We - // don't need to add them again here. - // TODO: Use LinkedHashSet to simplify the code. - continue; + if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi, context, + true /* checkDefaultAttribute */)) { + mInputMethodSet.add(imi); + added = true; } - if (isImeThatHasSubtypeOf(imi, systemLocale, true /* ignoreCountry */, - SUBTYPE_MODE_ANY)) { - retval.add(imi); + } + if (added) { + return this; + } + for (int i = 0; i < imis.size(); ++i) { + final InputMethodInfo imi = imis.get(i); + if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi, context, + false /* checkDefaultAttribute */)) { + mInputMethodSet.add(imi); } } + return this; } - // If one or more auxiliary input methods are available, OK to stop populating the list. - for (int i = 0; i < retval.size(); ++i) { - if (retval.get(i).isAuxiliaryIme()) { - return retval; - } + public boolean isEmpty() { + return mInputMethodSet.isEmpty(); } - for (int i = 0; i < imis.size(); ++i) { - final InputMethodInfo imi = imis.get(i); - if (isSystemAuxilialyImeThatHasAutomaticSubtype(imi)) { - retval.add(imi); - } + + @NonNull + public ArrayList<InputMethodInfo> build() { + return new ArrayList<>(mInputMethodSet); } - return retval; } - public static boolean isImeThatHasSubtypeOf(final InputMethodInfo imi, - final Locale locale, final boolean ignoreCountry, final String mode) { - if (locale == null) { - return false; - } - return containsSubtypeOf(imi, locale, ignoreCountry, mode); + private static InputMethodListBuilder getMinimumKeyboardSetWithoutSystemLocale( + final ArrayList<InputMethodInfo> imis, final Context context, + @Nullable final Locale fallbackLocale) { + // Before the system becomes ready, we pick up at least one keyboard in the following order. + // The first user (device owner) falls into this category. + // 1. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: true + // 2. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: true + // 3. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: false + // 4. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: false + // TODO: We should check isAsciiCapable instead of relying on fallbackLocale. + + final InputMethodListBuilder builder = new InputMethodListBuilder(); + builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale, + false /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale, + false /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + Slog.w(TAG, "No software keyboard is found. imis=" + Arrays.toString(imis.toArray()) + + " fallbackLocale=" + fallbackLocale); + return builder; + } + + private static InputMethodListBuilder getMinimumKeyboardSetWithSystemLocale( + final ArrayList<InputMethodInfo> imis, final Context context, + @Nullable final Locale systemLocale, @Nullable final Locale fallbackLocale) { + // Once the system becomes ready, we pick up at least one keyboard in the following order. + // Secondary users fall into this category in general. + // 1. checkDefaultAttribute: true, locale: systemLocale, checkCountry: true + // 2. checkDefaultAttribute: true, locale: systemLocale, checkCountry: false + // 3. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: true + // 4. checkDefaultAttribute: true, locale: fallbackLocale, checkCountry: false + // 5. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: true + // 6. checkDefaultAttribute: false, locale: fallbackLocale, checkCountry: false + // TODO: We should check isAsciiCapable instead of relying on fallbackLocale. + + final InputMethodListBuilder builder = new InputMethodListBuilder(); + builder.fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale, + false /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale, + false /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + builder.fillImes(imis, context, false /* checkDefaultAttribute */, fallbackLocale, + false /* checkCountry */, SUBTYPE_MODE_KEYBOARD); + if (!builder.isEmpty()) { + return builder; + } + Slog.w(TAG, "No software keyboard is found. imis=" + Arrays.toString(imis.toArray()) + + " systemLocale=" + systemLocale + " fallbackLocale=" + fallbackLocale); + return builder; + } + + public static ArrayList<InputMethodInfo> getDefaultEnabledImes(final Context context, + final boolean isSystemReady, final ArrayList<InputMethodInfo> imis) { + final Locale fallbackLocale = getFallbackLocaleForDefaultIme(imis, context); + if (!isSystemReady) { + // When the system is not ready, the system locale is not stable and reliable. Hence + // we will pick up IMEs that support software keyboard based on the fallback locale. + // Also pick up suitable IMEs regardless of the software keyboard support. + // (e.g. Voice IMEs) + return getMinimumKeyboardSetWithoutSystemLocale(imis, context, fallbackLocale) + .fillImes(imis, context, true /* checkDefaultAttribute */, fallbackLocale, + true /* checkCountry */, SUBTYPE_MODE_ANY) + .build(); + } + + // When the system is ready, we will primarily rely on the system locale, but also keep + // relying on the fallback locale as a last resort. + // Also pick up suitable IMEs regardless of the software keyboard support (e.g. Voice IMEs), + // then pick up suitable auxiliary IMEs when necessary (e.g. Voice IMEs with "automatic" + // subtype) + final Locale systemLocale = getSystemLocaleFromContext(context); + return getMinimumKeyboardSetWithSystemLocale(imis, context, systemLocale, fallbackLocale) + .fillImes(imis, context, true /* checkDefaultAttribute */, systemLocale, + true /* checkCountry */, SUBTYPE_MODE_ANY) + .fillAuxiliaryImes(imis, context) + .build(); } /** - * @deprecated Use {@link #isSystemIme(InputMethodInfo)} and - * {@link InputMethodInfo#isDefault(Context)} and - * {@link #isImeThatHasSubtypeOf(InputMethodInfo, Locale, boolean, String))} instead. + * @deprecated Use {@link #isSystemImeThatHasSubtypeOf(InputMethodInfo, Context, boolean, + * Locale, boolean, String)} instead. */ @Deprecated public static boolean isValidSystemDefaultIme( @@ -285,22 +410,25 @@ public class InputMethodUtils { } public static boolean containsSubtypeOf(final InputMethodInfo imi, - final Locale locale, final boolean ignoreCountry, final String mode) { + @Nullable final Locale locale, final boolean checkCountry, final String mode) { + if (locale == null) { + return false; + } final int N = imi.getSubtypeCount(); for (int i = 0; i < N; ++i) { final InputMethodSubtype subtype = imi.getSubtypeAt(i); - if (ignoreCountry) { - final Locale subtypeLocale = new Locale(getLanguageFromLocaleString( - subtype.getLocale())); - if (!subtypeLocale.getLanguage().equals(locale.getLanguage())) { - continue; - } - } else { + if (checkCountry) { // TODO: Use {@link Locale#toLanguageTag()} and // {@link Locale#forLanguageTag(languageTag)} instead. if (!TextUtils.equals(subtype.getLocale(), locale.toString())) { continue; } + } else { + final Locale subtypeLocale = new Locale(getLanguageFromLocaleString( + subtype.getLocale())); + if (!subtypeLocale.getLanguage().equals(locale.getLanguage())) { + continue; + } } if (mode == SUBTYPE_MODE_ANY || TextUtils.isEmpty(mode) || mode.equalsIgnoreCase(subtype.getMode())) { @@ -465,19 +593,9 @@ public class InputMethodUtils { return applicableSubtypes; } - private static List<InputMethodSubtype> getEnabledInputMethodSubtypeList( - Context context, InputMethodInfo imi, List<InputMethodSubtype> enabledSubtypes, - boolean allowsImplicitlySelectedSubtypes) { - if (allowsImplicitlySelectedSubtypes && enabledSubtypes.isEmpty()) { - enabledSubtypes = InputMethodUtils.getImplicitlyApplicableSubtypesLocked( - context.getResources(), imi); - } - return InputMethodSubtype.sort(context, 0, imi, enabledSubtypes); - } - /** * Returns the language component of a given locale string. - * TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(languageTag)} + * TODO: Use {@link Locale#toLanguageTag()} and {@link Locale#forLanguageTag(String)} */ public static String getLanguageFromLocaleString(String locale) { final int idx = locale.indexOf('_'); diff --git a/core/java/com/android/internal/net/VpnConfig.java b/core/java/com/android/internal/net/VpnConfig.java index 3d016be..c5d9db4 100644 --- a/core/java/com/android/internal/net/VpnConfig.java +++ b/core/java/com/android/internal/net/VpnConfig.java @@ -25,6 +25,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.net.LinkAddress; +import android.net.Network; import android.net.RouteInfo; import android.os.Parcel; import android.os.Parcelable; @@ -99,6 +100,7 @@ public class VpnConfig implements Parcelable { public boolean allowBypass; public boolean allowIPv4; public boolean allowIPv6; + public Network[] underlyingNetworks; public void updateAllowedFamilies(InetAddress address) { if (address instanceof Inet4Address) { @@ -162,6 +164,7 @@ public class VpnConfig implements Parcelable { out.writeInt(allowBypass ? 1 : 0); out.writeInt(allowIPv4 ? 1 : 0); out.writeInt(allowIPv6 ? 1 : 0); + out.writeTypedArray(underlyingNetworks, flags); } public static final Parcelable.Creator<VpnConfig> CREATOR = @@ -186,6 +189,7 @@ public class VpnConfig implements Parcelable { config.allowBypass = in.readInt() != 0; config.allowIPv4 = in.readInt() != 0; config.allowIPv6 = in.readInt() != 0; + config.underlyingNetworks = in.createTypedArray(Network.CREATOR); return config; } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index c00d209..20bb95e 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -94,7 +94,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 114 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 116 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -374,6 +374,10 @@ public final class BatteryStatsImpl extends BatteryStats { private int mPhoneServiceStateRaw = -1; private int mPhoneSimStateRaw = -1; + private int mNumConnectivityChange; + private int mLoadedNumConnectivityChange; + private int mUnpluggedNumConnectivityChange; + /* * Holds a SamplingTimer associated with each kernel wakelock name being tracked. */ @@ -2540,6 +2544,22 @@ public final class BatteryStatsImpl extends BatteryStats { addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_PROC_START, name, uid); } + public void noteProcessCrashLocked(String name, int uid) { + uid = mapUid(uid); + if (isOnBattery()) { + Uid u = getUidStatsLocked(uid); + u.getProcessStatsLocked(name).incNumCrashesLocked(); + } + } + + public void noteProcessAnrLocked(String name, int uid) { + uid = mapUid(uid); + if (isOnBattery()) { + Uid u = getUidStatsLocked(uid); + u.getProcessStatsLocked(name).incNumAnrsLocked(); + } + } + public void noteProcessStateLocked(String name, int uid, int state) { uid = mapUid(uid); final long elapsedRealtime = SystemClock.elapsedRealtime(); @@ -3109,6 +3129,14 @@ public final class BatteryStatsImpl extends BatteryStats { } } + public void noteConnectivityChangedLocked(int type, String extra) { + final long elapsedRealtime = SystemClock.elapsedRealtime(); + final long uptime = SystemClock.uptimeMillis(); + addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_CONNECTIVITY_CHANGED, + extra, type); + mNumConnectivityChange++; + } + public void noteMobileRadioPowerState(int powerState, long timestampNs) { final long elapsedRealtime = SystemClock.elapsedRealtime(); final long uptime = SystemClock.uptimeMillis(); @@ -3965,6 +3993,16 @@ public final class BatteryStatsImpl extends BatteryStats { return mLowPowerModeEnabledTimer.getCountLocked(which); } + @Override public int getNumConnectivityChange(int which) { + int val = mNumConnectivityChange; + if (which == STATS_CURRENT) { + val -= mLoadedNumConnectivityChange; + } else if (which == STATS_SINCE_UNPLUGGED) { + val -= mUnpluggedNumConnectivityChange; + } + return val; + } + @Override public long getPhoneOnTime(long elapsedRealtimeUs, int which) { return mPhoneOnTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } @@ -5374,6 +5412,16 @@ public final class BatteryStatsImpl extends BatteryStats { int mStarts; /** + * Number of times the process has crashed. + */ + int mNumCrashes; + + /** + * Number of times the process has had an ANR. + */ + int mNumAnrs; + + /** * The amount of user time loaded from a previous save. */ long mLoadedUserTime; @@ -5394,24 +5442,14 @@ public final class BatteryStatsImpl extends BatteryStats { int mLoadedStarts; /** - * The amount of user time loaded from the previous run. - */ - long mLastUserTime; - - /** - * The amount of system time loaded from the previous run. - */ - long mLastSystemTime; - - /** - * The amount of foreground time loaded from the previous run + * Number of times the process has crashed from a previous save. */ - long mLastForegroundTime; + int mLoadedNumCrashes; /** - * The number of times the process has started from the previous run. + * Number of times the process has had an ANR from a previous save. */ - int mLastStarts; + int mLoadedNumAnrs; /** * The amount of user time when last unplugged. @@ -5434,6 +5472,16 @@ public final class BatteryStatsImpl extends BatteryStats { int mUnpluggedStarts; /** + * Number of times the process has crashed before unplugged. + */ + int mUnpluggedNumCrashes; + + /** + * Number of times the process has had an ANR before unplugged. + */ + int mUnpluggedNumAnrs; + + /** * Current process state. */ int mProcessState = PROCESS_STATE_NONE; @@ -5453,6 +5501,8 @@ public final class BatteryStatsImpl extends BatteryStats { mUnpluggedSystemTime = mSystemTime; mUnpluggedForegroundTime = mForegroundTime; mUnpluggedStarts = mStarts; + mUnpluggedNumCrashes = mNumCrashes; + mUnpluggedNumAnrs = mNumAnrs; } public void onTimeStopped(long elapsedRealtime, long baseUptime, long baseRealtime) { @@ -5460,13 +5510,11 @@ public final class BatteryStatsImpl extends BatteryStats { void reset() { mUserTime = mSystemTime = mForegroundTime = 0; - mStarts = 0; + mStarts = mNumCrashes = mNumAnrs = 0; mLoadedUserTime = mLoadedSystemTime = mLoadedForegroundTime = 0; - mLoadedStarts = 0; - mLastUserTime = mLastSystemTime = mLastForegroundTime = 0; - mLastStarts = 0; + mLoadedStarts = mLoadedNumCrashes = mLoadedNumAnrs = 0; mUnpluggedUserTime = mUnpluggedSystemTime = mUnpluggedForegroundTime = 0; - mUnpluggedStarts = 0; + mUnpluggedStarts = mUnpluggedNumCrashes = mUnpluggedNumAnrs = 0; for (int i = 0; i < mSpeedBins.length; i++) { SamplingCounter c = mSpeedBins[i]; if (c != null) { @@ -5565,14 +5613,20 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(mSystemTime); out.writeLong(mForegroundTime); out.writeInt(mStarts); + out.writeInt(mNumCrashes); + out.writeInt(mNumAnrs); out.writeLong(mLoadedUserTime); out.writeLong(mLoadedSystemTime); out.writeLong(mLoadedForegroundTime); out.writeInt(mLoadedStarts); + out.writeInt(mLoadedNumCrashes); + out.writeInt(mLoadedNumAnrs); out.writeLong(mUnpluggedUserTime); out.writeLong(mUnpluggedSystemTime); out.writeLong(mUnpluggedForegroundTime); out.writeInt(mUnpluggedStarts); + out.writeInt(mUnpluggedNumCrashes); + out.writeInt(mUnpluggedNumAnrs); out.writeInt(mSpeedBins.length); for (int i = 0; i < mSpeedBins.length; i++) { @@ -5593,18 +5647,20 @@ public final class BatteryStatsImpl extends BatteryStats { mSystemTime = in.readLong(); mForegroundTime = in.readLong(); mStarts = in.readInt(); + mNumCrashes = in.readInt(); + mNumAnrs = in.readInt(); mLoadedUserTime = in.readLong(); mLoadedSystemTime = in.readLong(); mLoadedForegroundTime = in.readLong(); mLoadedStarts = in.readInt(); - mLastUserTime = 0; - mLastSystemTime = 0; - mLastForegroundTime = 0; - mLastStarts = 0; + mLoadedNumCrashes = in.readInt(); + mLoadedNumAnrs = in.readInt(); mUnpluggedUserTime = in.readLong(); mUnpluggedSystemTime = in.readLong(); mUnpluggedForegroundTime = in.readLong(); mUnpluggedStarts = in.readInt(); + mUnpluggedNumCrashes = in.readInt(); + mUnpluggedNumAnrs = in.readInt(); int bins = in.readInt(); int steps = getCpuSpeedSteps(); @@ -5635,6 +5691,14 @@ public final class BatteryStatsImpl extends BatteryStats { mStarts++; } + public void incNumCrashesLocked() { + mNumCrashes++; + } + + public void incNumAnrsLocked() { + mNumAnrs++; + } + @Override public boolean isActive() { return mActive; @@ -5684,6 +5748,28 @@ public final class BatteryStatsImpl extends BatteryStats { return val; } + @Override + public int getNumCrashes(int which) { + int val = mNumCrashes; + if (which == STATS_CURRENT) { + val -= mLoadedNumCrashes; + } else if (which == STATS_SINCE_UNPLUGGED) { + val -= mUnpluggedNumCrashes; + } + return val; + } + + @Override + public int getNumAnrs(int which) { + int val = mNumAnrs; + if (which == STATS_CURRENT) { + val -= mLoadedNumAnrs; + } else if (which == STATS_SINCE_UNPLUGGED) { + val -= mUnpluggedNumAnrs; + } + return val; + } + /* Called by ActivityManagerService when CPU times are updated. */ public void addSpeedStepTimes(long[] values) { for (int i = 0; i < mSpeedBins.length && i < values.length; i++) { @@ -6647,6 +6733,7 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i=0; i< NUM_BLUETOOTH_STATES; i++) { mBluetoothStateTimer[i].reset(false); } + mNumConnectivityChange = mLoadedNumConnectivityChange = mUnpluggedNumConnectivityChange = 0; for (int i=0; i<mUidStats.size(); i++) { if (mUidStats.valueAt(i).reset()) { @@ -6868,6 +6955,17 @@ public final class BatteryStatsImpl extends BatteryStats { } } + private void recordShutdownLocked(final long elapsedRealtimeMs, final long uptimeMs) { + if (mRecordingHistory) { + mHistoryCur.currentTime = System.currentTimeMillis(); + mLastRecordedClockTime = mHistoryCur.currentTime; + mLastRecordedClockRealtime = elapsedRealtimeMs; + addHistoryBufferLocked(elapsedRealtimeMs, uptimeMs, HistoryItem.CMD_SHUTDOWN, + mHistoryCur); + mHistoryCur.currentTime = 0; + } + } + // This should probably be exposed in the API, though it's not critical private static final int BATTERY_PLUGGED_NONE = 0; @@ -7540,6 +7638,7 @@ public final class BatteryStatsImpl extends BatteryStats { } public void shutdownLocked() { + recordShutdownLocked(SystemClock.elapsedRealtime(), SystemClock.uptimeMillis()); writeSyncLocked(); mShuttingDown = true; } @@ -7861,6 +7960,7 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i=0; i< NUM_BLUETOOTH_STATES; i++) { mBluetoothStateTimer[i].readSummaryFromParcelLocked(in); } + mNumConnectivityChange = mLoadedNumConnectivityChange = in.readInt(); mFlashlightOn = false; mFlashlightOnTimer.readSummaryFromParcelLocked(in); @@ -8022,6 +8122,8 @@ public final class BatteryStatsImpl extends BatteryStats { p.mSystemTime = p.mLoadedSystemTime = in.readLong(); p.mForegroundTime = p.mLoadedForegroundTime = in.readLong(); p.mStarts = p.mLoadedStarts = in.readInt(); + p.mNumCrashes = p.mLoadedNumCrashes = in.readInt(); + p.mNumAnrs = p.mLoadedNumAnrs = in.readInt(); int NSB = in.readInt(); if (NSB > 100) { Slog.w(TAG, "File corrupt: too many speed bins " + NSB); @@ -8143,6 +8245,7 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i=0; i< NUM_BLUETOOTH_STATES; i++) { mBluetoothStateTimer[i].writeSummaryFromParcelLocked(out, NOWREAL_SYS); } + out.writeInt(mNumConnectivityChange); mFlashlightOnTimer.writeSummaryFromParcelLocked(out, NOWREAL_SYS); out.writeInt(mKernelWakelockStats.size()); @@ -8326,6 +8429,8 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeLong(ps.mSystemTime); out.writeLong(ps.mForegroundTime); out.writeInt(ps.mStarts); + out.writeInt(ps.mNumCrashes); + out.writeInt(ps.mNumAnrs); final int N = ps.mSpeedBins.length; out.writeInt(N); for (int i=0; i<N; i++) { @@ -8444,6 +8549,9 @@ public final class BatteryStatsImpl extends BatteryStats { mBluetoothStateTimer[i] = new StopwatchTimer(null, -500-i, null, mOnBatteryTimeBase, in); } + mNumConnectivityChange = in.readInt(); + mLoadedNumConnectivityChange = in.readInt(); + mUnpluggedNumConnectivityChange = in.readInt(); mAudioOnNesting = 0; mAudioOnTimer = new StopwatchTimer(null, -7, null, mOnBatteryTimeBase); mVideoOnNesting = 0; @@ -8588,6 +8696,9 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i=0; i< NUM_BLUETOOTH_STATES; i++) { mBluetoothStateTimer[i].writeToParcel(out, uSecRealtime); } + out.writeInt(mNumConnectivityChange); + out.writeInt(mLoadedNumConnectivityChange); + out.writeInt(mUnpluggedNumConnectivityChange); mFlashlightOnTimer.writeToParcel(out, uSecRealtime); out.writeInt(mDischargeUnplugLevel); out.writeInt(mDischargePlugLevel); diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java index 17685fd..99286cb 100644 --- a/core/java/com/android/internal/os/HandlerCaller.java +++ b/core/java/com/android/internal/os/HandlerCaller.java @@ -49,6 +49,10 @@ public class HandlerCaller { mCallback = callback; } + public Handler getHandler() { + return mH; + } + public void executeOrSendMessage(Message msg) { // If we are calling this from the main thread, then we can call // right through. Otherwise, we need to send the message to the diff --git a/core/java/com/android/internal/os/ProcessCpuTracker.java b/core/java/com/android/internal/os/ProcessCpuTracker.java index 86f580d..b5338df 100644 --- a/core/java/com/android/internal/os/ProcessCpuTracker.java +++ b/core/java/com/android/internal/os/ProcessCpuTracker.java @@ -23,8 +23,11 @@ import android.os.Process; import android.os.StrictMode; import android.os.SystemClock; import android.util.Slog; + import com.android.internal.util.FastPrintWriter; +import libcore.io.IoUtils; + import java.io.File; import java.io.FileInputStream; import java.io.PrintWriter; @@ -325,7 +328,12 @@ public class ProcessCpuTracker { mBaseIdleTime = idletime; } - mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats); + final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads(); + try { + mCurPids = collectStats("/proc", -1, mFirst, mCurPids, mProcStats); + } finally { + StrictMode.setThreadPolicy(savedPolicy); + } final float[] loadAverages = mLoadAverageData; if (Process.readProcFile("/proc/loadavg", LOAD_AVERAGE_FORMAT, @@ -847,12 +855,7 @@ public class ProcessCpuTracker { } catch (java.io.FileNotFoundException e) { } catch (java.io.IOException e) { } finally { - if (is != null) { - try { - is.close(); - } catch (java.io.IOException e) { - } - } + IoUtils.closeQuietly(is); StrictMode.setThreadPolicy(savedPolicy); } return null; diff --git a/core/java/com/android/internal/policy/IKeyguardService.aidl b/core/java/com/android/internal/policy/IKeyguardService.aidl index 64f3bea..f93b1a1 100644 --- a/core/java/com/android/internal/policy/IKeyguardService.aidl +++ b/core/java/com/android/internal/policy/IKeyguardService.aidl @@ -15,47 +15,34 @@ */ package com.android.internal.policy; -import android.view.MotionEvent; - import com.android.internal.policy.IKeyguardShowCallback; +import com.android.internal.policy.IKeyguardStateCallback; import com.android.internal.policy.IKeyguardExitCallback; import android.os.Bundle; -interface IKeyguardService { - boolean isShowing(); - boolean isSecure(); - boolean isShowingAndNotOccluded(); - boolean isInputRestricted(); - boolean isDismissable(); - oneway void verifyUnlock(IKeyguardExitCallback callback); - oneway void keyguardDone(boolean authenticated, boolean wakeup); - +oneway interface IKeyguardService { /** * Sets the Keyguard as occluded when a window dismisses the Keyguard with flag * FLAG_SHOW_ON_LOCK_SCREEN. * * @param isOccluded Whether the Keyguard is occluded by another window. - * @return See IKeyguardServiceConstants.KEYGUARD_SERVICE_SET_OCCLUDED_*. This is needed because - * PhoneWindowManager needs to set these flags immediately and can't wait for the - * Keyguard thread to pick it up. In the hidden case, PhoneWindowManager is solely - * responsible to make sure that the flags are unset. */ - int setOccluded(boolean isOccluded); - - oneway void dismiss(); - oneway void onDreamingStarted(); - oneway void onDreamingStopped(); - oneway void onScreenTurnedOff(int reason); - oneway void onScreenTurnedOn(IKeyguardShowCallback callback); - oneway void setKeyguardEnabled(boolean enabled); - oneway void onSystemReady(); - oneway void doKeyguardTimeout(in Bundle options); - oneway void setCurrentUser(int userId); - oneway void showAssistant(); - oneway void dispatch(in MotionEvent event); - oneway void launchCamera(); - oneway void onBootCompleted(); + void setOccluded(boolean isOccluded); + + void addStateMonitorCallback(IKeyguardStateCallback callback); + void verifyUnlock(IKeyguardExitCallback callback); + void keyguardDone(boolean authenticated, boolean wakeup); + void dismiss(); + void onDreamingStarted(); + void onDreamingStopped(); + void onScreenTurnedOff(int reason); + void onScreenTurnedOn(IKeyguardShowCallback callback); + void setKeyguardEnabled(boolean enabled); + void onSystemReady(); + void doKeyguardTimeout(in Bundle options); + void setCurrentUser(int userId); + void onBootCompleted(); /** * Notifies that the activity behind has now been drawn and it's safe to remove the wallpaper @@ -64,11 +51,11 @@ interface IKeyguardService { * @param startTime the start time of the animation in uptime milliseconds * @param fadeoutDuration the duration of the exit animation, in milliseconds */ - oneway void startKeyguardExitAnimation(long startTime, long fadeoutDuration); + void startKeyguardExitAnimation(long startTime, long fadeoutDuration); /** * Notifies the Keyguard that the activity that was starting has now been drawn and it's safe * to start the keyguard dismiss sequence. */ - oneway void onActivityDrawn(); + void onActivityDrawn(); } diff --git a/core/java/com/android/internal/policy/IKeyguardServiceConstants.java b/core/java/com/android/internal/policy/IKeyguardServiceConstants.java deleted file mode 100644 index b88174a..0000000 --- a/core/java/com/android/internal/policy/IKeyguardServiceConstants.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.internal.policy; - -/** - * @hide - */ -public class IKeyguardServiceConstants { - - /** - * Constant for {@link com.android.internal.policy.IKeyguardService#setHidden(boolean)}: - * Don't change the keyguard window flags. - */ - public static final int KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_NONE = 0; - - /** - * Constant for {@link com.android.internal.policy.IKeyguardService#setHidden(boolean)}: - * Set the keyguard window flags to FLAG_SHOW_WALLPAPER and PRIVATE_FLAG_KEYGUARD. - */ - public static final int KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_SET_FLAGS = 1; - - /** - * Constant for {@link com.android.internal.policy.IKeyguardService#setHidden(boolean)}: - * Unset the keyguard window flags to FLAG_SHOW_WALLPAPER and PRIVATE_FLAG_KEYGUARD. - */ - public static final int KEYGUARD_SERVICE_SET_OCCLUDED_RESULT_UNSET_FLAGS = 2; -} diff --git a/core/java/com/android/internal/widget/ILockSettingsObserver.aidl b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl index 6c354d8..db3b40b 100644 --- a/core/java/com/android/internal/widget/ILockSettingsObserver.aidl +++ b/core/java/com/android/internal/policy/IKeyguardStateCallback.aidl @@ -13,10 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package com.android.internal.policy; -package com.android.internal.widget; - -/** {@hide} */ -oneway interface ILockSettingsObserver { - void onLockSettingChanged(in String key, in int userId); -} +interface IKeyguardStateCallback { + void onShowingStateChanged(boolean showing); + void onSimSecureStateChanged(boolean simSecure); + void onInputRestrictedStateChanged(boolean inputRestricted); +}
\ No newline at end of file diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 57472f8..a3c0db4 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -42,5 +42,6 @@ oneway interface IStatusBar void toggleRecentApps(); void preloadRecentApps(); void cancelPreloadRecentApps(); + void showScreenPinningRequest(); } diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index 8794d31..40c009f 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -40,9 +40,12 @@ interface IStatusBarService // You need the STATUS_BAR_SERVICE permission void registerStatusBar(IStatusBar callbacks, out StatusBarIconList iconList, out int[] switches, out List<IBinder> binders); - void onPanelRevealed(); + void onPanelRevealed(boolean clearNotificationEffects); void onPanelHidden(); + // Mark current notifications as "seen" and stop ringing, vibrating, blinking. + void clearNotificationEffects(); void onNotificationClick(String key); + void onNotificationActionClick(String key, int actionIndex); void onNotificationError(String pkg, String tag, int id, int uid, int initialPid, String message, int userId); void onClearAllNotifications(int userId); @@ -50,7 +53,7 @@ interface IStatusBarService void onNotificationVisibilityChanged( in String[] newlyVisibleKeys, in String[] noLongerVisibleKeys); void onNotificationExpansionChanged(in String key, in boolean userAction, in boolean expanded); - void setSystemUiVisibility(int vis, int mask); + void setSystemUiVisibility(int vis, int mask, String cause); void setWindowState(int window, int state); void showRecentApps(boolean triggeredFromAltTab); diff --git a/core/java/com/android/internal/util/ArrayUtils.java b/core/java/com/android/internal/util/ArrayUtils.java index 8e786da..f908fcb 100644 --- a/core/java/com/android/internal/util/ArrayUtils.java +++ b/core/java/com/android/internal/util/ArrayUtils.java @@ -128,6 +128,20 @@ public class ArrayUtils } /** + * Checks if given array is null or has zero elements. + */ + public static boolean isEmpty(int[] array) { + return array == null || array.length == 0; + } + + /** + * Checks if given array is null or has zero elements. + */ + public static boolean isEmpty(long[] array) { + return array == null || array.length == 0; + } + + /** * Checks that value is present as at least one of the elements of the array. * @param array the array to check in * @param value the value to check for @@ -157,6 +171,7 @@ public class ArrayUtils * Test if all {@code check} items are contained in {@code array}. */ public static <T> boolean containsAll(T[] array, T[] check) { + if (check == null) return true; for (T checkItem : check) { if (!contains(array, checkItem)) { return false; diff --git a/core/java/com/android/internal/util/FastPrintWriter.java b/core/java/com/android/internal/util/FastPrintWriter.java index c70a243..c74fea0 100644 --- a/core/java/com/android/internal/util/FastPrintWriter.java +++ b/core/java/com/android/internal/util/FastPrintWriter.java @@ -15,7 +15,7 @@ import java.nio.charset.CoderResult; import java.nio.charset.CodingErrorAction; public class FastPrintWriter extends PrintWriter { - private static Writer sDummyWriter = new Writer() { + private static class DummyWriter extends Writer { @Override public void close() throws IOException { UnsupportedOperationException ex @@ -100,7 +100,7 @@ public class FastPrintWriter extends PrintWriter { * if {@code out} is {@code null}. */ public FastPrintWriter(OutputStream out, boolean autoFlush, int bufferLen) { - super(sDummyWriter, autoFlush); + super(new DummyWriter(), autoFlush); if (out == null) { throw new NullPointerException("out is null"); } @@ -169,7 +169,7 @@ public class FastPrintWriter extends PrintWriter { * if {@code wr} is {@code null}. */ public FastPrintWriter(Writer wr, boolean autoFlush, int bufferLen) { - super(sDummyWriter, autoFlush); + super(new DummyWriter(), autoFlush); if (wr == null) { throw new NullPointerException("wr is null"); } @@ -212,7 +212,7 @@ public class FastPrintWriter extends PrintWriter { * if {@code pr} is {@code null}. */ public FastPrintWriter(Printer pr, int bufferLen) { - super(sDummyWriter, true); + super(new DummyWriter(), true); if (pr == null) { throw new NullPointerException("pr is null"); } diff --git a/core/java/com/android/internal/util/MemInfoReader.java b/core/java/com/android/internal/util/MemInfoReader.java index 5f240f7..b71fa06 100644 --- a/core/java/com/android/internal/util/MemInfoReader.java +++ b/core/java/com/android/internal/util/MemInfoReader.java @@ -34,40 +34,65 @@ public final class MemInfoReader { } } + /** + * Total amount of RAM available to the kernel. + */ public long getTotalSize() { return mInfos[Debug.MEMINFO_TOTAL] * 1024; } + /** + * Amount of RAM that is not being used for anything. + */ public long getFreeSize() { return mInfos[Debug.MEMINFO_FREE] * 1024; } + /** + * Amount of RAM that the kernel is being used for caches, not counting caches + * that are mapped in to processes. + */ public long getCachedSize() { - return mInfos[Debug.MEMINFO_CACHED] * 1024; + return getCachedSizeKb() * 1024; } + /** + * Amount of RAM that is in use by the kernel for actual allocations. + */ + public long getKernelUsedSize() { + return getKernelUsedSizeKb() * 1024; + } + + /** + * Total amount of RAM available to the kernel. + */ public long getTotalSizeKb() { return mInfos[Debug.MEMINFO_TOTAL]; } + /** + * Amount of RAM that is not being used for anything. + */ public long getFreeSizeKb() { return mInfos[Debug.MEMINFO_FREE]; } + /** + * Amount of RAM that the kernel is being used for caches, not counting caches + * that are mapped in to processes. + */ public long getCachedSizeKb() { - return mInfos[Debug.MEMINFO_CACHED]; - } - - public long getBuffersSizeKb() { - return mInfos[Debug.MEMINFO_BUFFERS]; + return mInfos[Debug.MEMINFO_BUFFERS] + + mInfos[Debug.MEMINFO_CACHED] - mInfos[Debug.MEMINFO_MAPPED]; } - public long getShmemSizeKb() { - return mInfos[Debug.MEMINFO_SHMEM]; - } - - public long getSlabSizeKb() { - return mInfos[Debug.MEMINFO_SLAB]; + /** + * Amount of RAM that is in use by the kernel for actual allocations. + */ + public long getKernelUsedSizeKb() { + return mInfos[Debug.MEMINFO_SHMEM] + mInfos[Debug.MEMINFO_SLAB] + + mInfos[Debug.MEMINFO_VM_ALLOC_USED] + mInfos[Debug.MEMINFO_PAGE_TABLES] + + mInfos[Debug.MEMINFO_KERNEL_STACK]; } public long getSwapTotalSizeKb() { @@ -81,4 +106,8 @@ public final class MemInfoReader { public long getZramTotalSizeKb() { return mInfos[Debug.MEMINFO_ZRAM_TOTAL]; } + + public long[] getRawInfo() { + return mInfos; + } } diff --git a/core/java/com/android/internal/util/StateMachine.java b/core/java/com/android/internal/util/StateMachine.java index d26f79e..916f19d 100644 --- a/core/java/com/android/internal/util/StateMachine.java +++ b/core/java/com/android/internal/util/StateMachine.java @@ -25,6 +25,7 @@ import android.util.Log; import java.io.FileDescriptor; import java.io.PrintWriter; +import java.io.StringWriter; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; @@ -1940,15 +1941,27 @@ public class StateMachine { * @param args */ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + // Cannot just invoke pw.println(this.toString()) because if the + // resulting string is to long it won't be displayed. pw.println(getName() + ":"); pw.println(" total records=" + getLogRecCount()); for (int i = 0; i < getLogRecSize(); i++) { - pw.printf(" rec[%d]: %s\n", i, getLogRec(i).toString()); + pw.println(" rec[" + i + "]: " + getLogRec(i).toString()); pw.flush(); } pw.println("curState=" + getCurrentState().getName()); } + @Override + public String toString() { + StringWriter sr = new StringWriter(); + PrintWriter pr = new PrintWriter(sr); + dump(null, pr, null); + pr.flush(); + pr.close(); + return sr.toString(); + } + /** * Log with debug and add to the LogRecords. * diff --git a/core/java/com/android/internal/util/XmlUtils.java b/core/java/com/android/internal/util/XmlUtils.java index e9baaa8..2bd607c 100644 --- a/core/java/com/android/internal/util/XmlUtils.java +++ b/core/java/com/android/internal/util/XmlUtils.java @@ -520,7 +520,7 @@ public class XmlUtils { * Flatten a String[] into an XmlSerializer. The list can later be read back * with readThisStringArrayXml(). * - * @param val The long array to be flattened. + * @param val The String array to be flattened. * @param name Name attribute to include with this array's tag, or null for * none. * @param out XmlSerializer to write the array into. @@ -556,6 +556,45 @@ public class XmlUtils { } /** + * Flatten a boolean[] into an XmlSerializer. The list can later be read back + * with readThisBooleanArrayXml(). + * + * @param val The boolean array to be flattened. + * @param name Name attribute to include with this array's tag, or null for + * none. + * @param out XmlSerializer to write the array into. + * + * @see #writeMapXml + * @see #writeValueXml + * @see #readThisIntArrayXml + */ + public static final void writeBooleanArrayXml(boolean[] val, String name, XmlSerializer out) + throws XmlPullParserException, java.io.IOException { + + if (val == null) { + out.startTag(null, "null"); + out.endTag(null, "null"); + return; + } + + out.startTag(null, "boolean-array"); + if (name != null) { + out.attribute(null, "name", name); + } + + final int N = val.length; + out.attribute(null, "num", Integer.toString(N)); + + for (int i=0; i<N; i++) { + out.startTag(null, "item"); + out.attribute(null, "value", Boolean.toString(val[i])); + out.endTag(null, "item"); + } + + out.endTag(null, "boolean-array"); + } + + /** * Flatten an object's value into an XmlSerializer. The value can later * be read back with readThisValueXml(). * @@ -636,6 +675,9 @@ public class XmlUtils { } else if (v instanceof String[]) { writeStringArrayXml((String[])v, name, out); return; + } else if (v instanceof boolean[]) { + writeBooleanArrayXml((boolean[])v, name, out); + return; } else if (v instanceof Map) { writeMapXml((Map)v, name, out); return; @@ -1169,6 +1211,66 @@ public class XmlUtils { } /** + * Read a boolean[] object from an XmlPullParser. The XML data could + * previously have been generated by writeBooleanArrayXml(). The XmlPullParser + * must be positioned <em>after</em> the tag that begins the list. + * + * @param parser The XmlPullParser from which to read the list data. + * @param endTag Name of the tag that will end the list, usually "string-array". + * @param name An array of one string, used to return the name attribute + * of the list's tag. + * + * @return Returns a newly generated boolean[]. + * + * @see #readListXml + */ + public static final boolean[] readThisBooleanArrayXml(XmlPullParser parser, String endTag, + String[] name) throws XmlPullParserException, java.io.IOException { + + int num; + try { + num = Integer.parseInt(parser.getAttributeValue(null, "num")); + } catch (NullPointerException e) { + throw new XmlPullParserException("Need num attribute in string-array"); + } catch (NumberFormatException e) { + throw new XmlPullParserException("Not a number in num attribute in string-array"); + } + parser.next(); + + boolean[] array = new boolean[num]; + int i = 0; + + int eventType = parser.getEventType(); + do { + if (eventType == parser.START_TAG) { + if (parser.getName().equals("item")) { + try { + array[i] = Boolean.valueOf(parser.getAttributeValue(null, "value")); + } catch (NullPointerException e) { + throw new XmlPullParserException("Need value attribute in item"); + } catch (NumberFormatException e) { + throw new XmlPullParserException("Not a number in value attribute in item"); + } + } else { + throw new XmlPullParserException("Expected item tag at: " + parser.getName()); + } + } else if (eventType == parser.END_TAG) { + if (parser.getName().equals(endTag)) { + return array; + } else if (parser.getName().equals("item")) { + i++; + } else { + throw new XmlPullParserException("Expected " + endTag + " end tag at: " + + parser.getName()); + } + } + eventType = parser.next(); + } while (eventType != parser.END_DOCUMENT); + + throw new XmlPullParserException("Document ended before " + endTag + " end tag"); + } + + /** * Read a flattened object from an XmlPullParser. The XML data could * previously have been written with writeMapXml(), writeListXml(), or * writeValueXml(). The XmlPullParser must be positioned <em>at</em> the @@ -1259,6 +1361,11 @@ public class XmlUtils { name[0] = valueName; //System.out.println("Returning value for " + valueName + ": " + res); return res; + } else if (tagName.equals("boolean-array")) { + res = readThisBooleanArrayXml(parser, "boolean-array", name); + name[0] = valueName; + //System.out.println("Returning value for " + valueName + ": " + res); + return res; } else if (tagName.equals("map")) { parser.next(); res = readThisMapXml(parser, "map", name); diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java index 50a7a5e..993ab58 100644 --- a/core/java/com/android/internal/view/BaseIWindow.java +++ b/core/java/com/android/internal/view/BaseIWindow.java @@ -102,4 +102,8 @@ public class BaseIWindow extends IWindow.Stub { @Override public void doneAnimating() { } + + @Override + public void dispatchWindowShown() { + } } diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java index 9c1b558..433ec73 100644 --- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java +++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java @@ -1,3 +1,18 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package com.android.internal.view; import android.view.InputQueue; @@ -10,4 +25,5 @@ public interface RootViewSurfaceTaker { void setSurfaceFormat(int format); void setSurfaceKeepScreenOn(boolean keepOn); InputQueue.Callback willYouTakeTheInputQueue(); + void onRootViewScrollYChanged(int scrollY); } diff --git a/core/java/com/android/internal/view/menu/MenuPopupHelper.java b/core/java/com/android/internal/view/menu/MenuPopupHelper.java index 40f58e9..99bb1ac 100644 --- a/core/java/com/android/internal/view/menu/MenuPopupHelper.java +++ b/core/java/com/android/internal/view/menu/MenuPopupHelper.java @@ -54,6 +54,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On private final boolean mOverflowOnly; private final int mPopupMaxWidth; private final int mPopupStyleAttr; + private final int mPopupStyleRes; private View mAnchorView; private ListPopupWindow mPopup; @@ -73,21 +74,27 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On private int mDropDownGravity = Gravity.NO_GRAVITY; public MenuPopupHelper(Context context, MenuBuilder menu) { - this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle); + this(context, menu, null, false, com.android.internal.R.attr.popupMenuStyle, 0); } public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) { - this(context, menu, anchorView, false, com.android.internal.R.attr.popupMenuStyle); + this(context, menu, anchorView, false, com.android.internal.R.attr.popupMenuStyle, 0); } public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView, boolean overflowOnly, int popupStyleAttr) { + this(context, menu, anchorView, overflowOnly, popupStyleAttr, 0); + } + + public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView, + boolean overflowOnly, int popupStyleAttr, int popupStyleRes) { mContext = context; mInflater = LayoutInflater.from(context); mMenu = menu; mAdapter = new MenuAdapter(mMenu); mOverflowOnly = overflowOnly; mPopupStyleAttr = popupStyleAttr; + mPopupStyleRes = popupStyleRes; final Resources res = context.getResources(); mPopupMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2, @@ -122,7 +129,7 @@ public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.On } public boolean tryShow() { - mPopup = new ListPopupWindow(mContext, null, mPopupStyleAttr); + mPopup = new ListPopupWindow(mContext, null, mPopupStyleAttr, mPopupStyleRes); mPopup.setOnDismissListener(this); mPopup.setOnItemClickListener(this); mPopup.setAdapter(mAdapter); diff --git a/core/java/com/android/internal/widget/AccountItemView.java b/core/java/com/android/internal/widget/AccountItemView.java new file mode 100644 index 0000000..a521428 --- /dev/null +++ b/core/java/com/android/internal/widget/AccountItemView.java @@ -0,0 +1,102 @@ +/* +* Copyright (C) 2011-2014 The Android Open Source Project +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.android.internal.widget; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.internal.R; +import com.android.internal.widget.AccountViewAdapter.AccountElements; + + +/** + * An LinearLayout view, to show Accounts elements. + */ +public class AccountItemView extends LinearLayout { + + private ImageView mAccountIcon; + private TextView mAccountName; + private TextView mAccountNumber; + + /** + * Constructor. + */ + public AccountItemView(Context context) { + this(context, null); + } + + /** + * Constructor. + */ + public AccountItemView(Context context, AttributeSet attrs) { + super(context, attrs); + LayoutInflater inflator = (LayoutInflater) + context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View view = inflator.inflate(R.layout.simple_account_item, null); + addView(view); + initViewItem(view); + } + + private void initViewItem(View view) { + mAccountIcon = (ImageView)view.findViewById(android.R.id.icon); + mAccountName = (TextView)view.findViewById(android.R.id.title); + mAccountNumber = (TextView)view.findViewById(android.R.id.summary); + } + + public void setViewItem(AccountElements element) { + Drawable drawable = element.getDrawable(); + if (drawable != null) { + setAccountIcon(drawable); + } else { + setAccountIcon(element.getIcon()); + } + setAccountName(element.getName()); + setAccountNumber(element.getNumber()); + } + + public void setAccountIcon(int resId) { + mAccountIcon.setImageResource(resId); + } + + public void setAccountIcon(Drawable drawable) { + mAccountIcon.setBackgroundDrawable(drawable); + } + + public void setAccountName(String name) { + setText(mAccountName, name); + } + + public void setAccountNumber(String number) { + setText(mAccountNumber, number); + } + + private void setText(TextView view, String text) { + if (TextUtils.isEmpty(text)) { + view.setVisibility(View.GONE); + } else { + view.setText(text); + view.setVisibility(View.VISIBLE); + } + } +} diff --git a/core/java/com/android/internal/widget/AccountViewAdapter.java b/core/java/com/android/internal/widget/AccountViewAdapter.java new file mode 100644 index 0000000..8a7a9a6 --- /dev/null +++ b/core/java/com/android/internal/widget/AccountViewAdapter.java @@ -0,0 +1,127 @@ +/* +* Copyright (C) 2011-2014 The Android Open Source Project. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +package com.android.internal.widget; + +import android.content.Context; +import android.graphics.drawable.Drawable; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; + +import java.util.List; + +public class AccountViewAdapter extends BaseAdapter { + + private List<AccountElements> mData; + private Context mContext; + + /** + * Constructor + * + * @param context The context where the View associated with this Adapter is running + * @param data A list with AccountElements data type. The list contains the data of each + * account and the each member of AccountElements will correspond to one item view. + */ + public AccountViewAdapter(Context context, final List<AccountElements> data) { + mContext = context; + mData = data; + } + + @Override + public int getCount() { + return mData.size(); + } + + @Override + public Object getItem(int position) { + return mData.get(position); + } + + @Override + public long getItemId(int position) { + return position; + } + + public void updateData(final List<AccountElements> data) { + mData = data; + notifyDataSetChanged(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + AccountItemView view; + if (convertView == null) { + view = new AccountItemView(mContext); + } else { + view = (AccountItemView) convertView; + } + AccountElements elements = (AccountElements) getItem(position); + view.setViewItem(elements); + return view; + } + + public static class AccountElements { + private int mIcon; + private Drawable mDrawable; + private String mName; + private String mNumber; + + /** + * Constructor + * A structure with basic element of an Account, icon, name and number + * + * @param icon Account icon id + * @param name Account name + * @param num Account number + */ + public AccountElements(int icon, String name, String number) { + this(icon, null, name, number); + } + + /** + * Constructor + * A structure with basic element of an Account, drawable, name and number + * + * @param drawable Account drawable + * @param name Account name + * @param num Account number + */ + public AccountElements(Drawable drawable, String name, String number) { + this(0, drawable, name, number); + } + + private AccountElements(int icon, Drawable drawable, String name, String number) { + mIcon = icon; + mDrawable = drawable; + mName = name; + mNumber = number; + } + + public int getIcon() { + return mIcon; + } + public String getName() { + return mName; + } + public String getNumber() { + return mNumber; + } + public Drawable getDrawable() { + return mDrawable; + } + } +} diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index 062a9b1..7c671e8 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -241,7 +241,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi if (!mSplitActionBar) { menu.addMenuPresenter(mActionMenuPresenter, mPopupContext); mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this); - mMenuView.setBackgroundDrawable(null); + mMenuView.setBackground(null); addView(mMenuView, layoutParams); } else { // Allow full screen width in split mode. diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 91e5330..654d08b 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -19,8 +19,6 @@ package com.android.internal.widget; import android.animation.LayoutTransition; import android.app.ActionBar; import android.content.Context; -import android.content.pm.ApplicationInfo; -import android.content.pm.PackageManager; import android.content.res.Configuration; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; @@ -29,9 +27,7 @@ import android.os.Parcelable; import android.text.Layout; import android.text.TextUtils; import android.util.AttributeSet; -import android.util.TypedValue; import android.view.CollapsibleActionView; -import android.view.ContextThemeWrapper; import android.view.Gravity; import android.view.LayoutInflater; import android.view.Menu; @@ -111,10 +107,10 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { private int mProgressBarPadding; private int mItemPadding; - private int mTitleStyleRes; - private int mSubtitleStyleRes; - private int mProgressStyle; - private int mIndeterminateProgressStyle; + private final int mTitleStyleRes; + private final int mSubtitleStyleRes; + private final int mProgressStyle; + private final int mIndeterminateProgressStyle; private boolean mUserTitle; private boolean mIncludeTabs; @@ -1345,6 +1341,22 @@ public class ActionBarView extends AbsActionBarView implements DecorToolbar { updateHomeAccessibility(mUpGoerFive.isEnabled()); } + @Override + public void setMenuCallbacks(MenuPresenter.Callback presenterCallback, + MenuBuilder.Callback menuBuilderCallback) { + if (mActionMenuPresenter != null) { + mActionMenuPresenter.setCallback(presenterCallback); + } + if (mOptionsMenu != null) { + mOptionsMenu.setCallback(menuBuilderCallback); + } + } + + @Override + public Menu getMenu() { + return mOptionsMenu; + } + static class SavedState extends BaseSavedState { int expandedMenuItemId; boolean isOverflowOpen; diff --git a/core/java/com/android/internal/widget/DecorToolbar.java b/core/java/com/android/internal/widget/DecorToolbar.java index f89f0b7..fb413b5 100644 --- a/core/java/com/android/internal/widget/DecorToolbar.java +++ b/core/java/com/android/internal/widget/DecorToolbar.java @@ -27,6 +27,8 @@ import android.view.ViewGroup; import android.view.Window; import android.widget.AdapterView; import android.widget.SpinnerAdapter; + +import com.android.internal.view.menu.MenuBuilder; import com.android.internal.view.menu.MenuPresenter; /** @@ -93,4 +95,11 @@ public interface DecorToolbar { void setDefaultNavigationIcon(Drawable icon); void saveHierarchyState(SparseArray<Parcelable> toolbarStates); void restoreHierarchyState(SparseArray<Parcelable> toolbarStates); + void setBackgroundDrawable(Drawable d); + int getHeight(); + void setVisibility(int visible); + int getVisibility(); + void setMenuCallbacks(MenuPresenter.Callback presenterCallback, + MenuBuilder.Callback menuBuilderCallback); + Menu getMenu(); } diff --git a/core/java/com/android/internal/widget/ExploreByTouchHelper.java b/core/java/com/android/internal/widget/ExploreByTouchHelper.java index 11c4ca1..0e046cb 100644 --- a/core/java/com/android/internal/widget/ExploreByTouchHelper.java +++ b/core/java/com/android/internal/widget/ExploreByTouchHelper.java @@ -19,6 +19,7 @@ package com.android.internal.widget; import android.content.Context; import android.graphics.Rect; import android.os.Bundle; +import android.util.IntArray; import android.view.accessibility.*; import android.view.MotionEvent; import android.view.View; @@ -26,11 +27,9 @@ import android.view.ViewParent; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityNodeInfo; +import android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction; import android.view.accessibility.AccessibilityNodeProvider; -import java.util.LinkedList; -import java.util.List; - /** * ExploreByTouchHelper is a utility class for implementing accessibility * support in custom {@link android.view.View}s that represent a collection of View-like @@ -54,14 +53,20 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { /** Default class name used for virtual views. */ private static final String DEFAULT_CLASS_NAME = View.class.getName(); - // Temporary, reusable data structures. - private final Rect mTempScreenRect = new Rect(); - private final Rect mTempParentRect = new Rect(); - private final Rect mTempVisibleRect = new Rect(); - private final int[] mTempGlobalRect = new int[2]; + /** Default bounds used to determine if the client didn't set any. */ + private static final Rect INVALID_PARENT_BOUNDS = new Rect( + Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MIN_VALUE, Integer.MIN_VALUE); + + // Lazily-created temporary data structures used when creating nodes. + private Rect mTempScreenRect; + private Rect mTempParentRect; + private int[] mTempGlobalRect; + + /** Lazily-created temporary data structure used to compute visibility. */ + private Rect mTempVisibleRect; - /** View's context **/ - private Context mContext; + /** Lazily-created temporary data structure used to obtain child IDs. */ + private IntArray mTempArray; /** System accessibility manager, used to check state and send events. */ private final AccessibilityManager mManager; @@ -69,6 +74,9 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { /** View whose internal structure is exposed through this helper. */ private final View mView; + /** Context of the host view. **/ + private final Context mContext; + /** Node provider that handles creating nodes and performing actions. */ private ExploreByTouchNodeProvider mNodeProvider; @@ -328,11 +336,17 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { onInitializeAccessibilityNodeInfo(mView, node); // Add the virtual descendants. - final LinkedList<Integer> virtualViewIds = new LinkedList<Integer>(); + if (mTempArray == null) { + mTempArray = new IntArray(); + } else { + mTempArray.clear(); + } + final IntArray virtualViewIds = mTempArray; getVisibleVirtualViews(virtualViewIds); - for (Integer childVirtualViewId : virtualViewIds) { - node.addChild(mView, childVirtualViewId); + final int N = virtualViewIds.size(); + for (int i = 0; i < N; i++) { + node.addChild(mView, virtualViewIds.get(i)); } return node; @@ -367,11 +381,17 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { * @return An {@link AccessibilityNodeInfo} for the specified item. */ private AccessibilityNodeInfo createNodeForChild(int virtualViewId) { + ensureTempRects(); + final Rect tempParentRect = mTempParentRect; + final int[] tempGlobalRect = mTempGlobalRect; + final Rect tempScreenRect = mTempScreenRect; + final AccessibilityNodeInfo node = AccessibilityNodeInfo.obtain(); // Ensure the client has good defaults. node.setEnabled(true); node.setClassName(DEFAULT_CLASS_NAME); + node.setBoundsInParent(INVALID_PARENT_BOUNDS); // Allow the client to populate the node. onPopulateNodeForVirtualView(virtualViewId, node); @@ -382,8 +402,8 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { + "populateNodeForVirtualViewId()"); } - node.getBoundsInParent(mTempParentRect); - if (mTempParentRect.isEmpty()) { + node.getBoundsInParent(tempParentRect); + if (tempParentRect.equals(INVALID_PARENT_BOUNDS)) { throw new RuntimeException("Callbacks must set parent bounds in " + "populateNodeForVirtualViewId()"); } @@ -406,29 +426,35 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { // Manage internal accessibility focus state. if (mFocusedVirtualViewId == virtualViewId) { node.setAccessibilityFocused(true); - node.addAction(AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS); + node.addAction(AccessibilityAction.ACTION_CLEAR_ACCESSIBILITY_FOCUS); } else { node.setAccessibilityFocused(false); - node.addAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS); + node.addAction(AccessibilityAction.ACTION_ACCESSIBILITY_FOCUS); } // Set the visibility based on the parent bound. - if (intersectVisibleToUser(mTempParentRect)) { + if (intersectVisibleToUser(tempParentRect)) { node.setVisibleToUser(true); - node.setBoundsInParent(mTempParentRect); + node.setBoundsInParent(tempParentRect); } // Calculate screen-relative bound. - mView.getLocationOnScreen(mTempGlobalRect); - final int offsetX = mTempGlobalRect[0]; - final int offsetY = mTempGlobalRect[1]; - mTempScreenRect.set(mTempParentRect); - mTempScreenRect.offset(offsetX, offsetY); - node.setBoundsInScreen(mTempScreenRect); + mView.getLocationOnScreen(tempGlobalRect); + final int offsetX = tempGlobalRect[0]; + final int offsetY = tempGlobalRect[1]; + tempScreenRect.set(tempParentRect); + tempScreenRect.offset(offsetX, offsetY); + node.setBoundsInScreen(tempScreenRect); return node; } + private void ensureTempRects() { + mTempGlobalRect = new int[2]; + mTempParentRect = new Rect(); + mTempScreenRect = new Rect(); + } + private boolean performAction(int virtualViewId, int action, Bundle arguments) { switch (virtualViewId) { case View.NO_ID: @@ -446,13 +472,13 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { switch (action) { case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS: - return manageFocusForChild(virtualViewId, action, arguments); + return manageFocusForChild(virtualViewId, action); default: return onPerformActionForVirtualView(virtualViewId, action, arguments); } } - private boolean manageFocusForChild(int virtualViewId, int action, Bundle arguments) { + private boolean manageFocusForChild(int virtualViewId, int action) { switch (action) { case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS: return requestAccessibilityFocus(virtualViewId); @@ -498,12 +524,16 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { } // If no portion of the parent is visible, this view is not visible. - if (!mView.getLocalVisibleRect(mTempVisibleRect)) { + if (mTempVisibleRect == null) { + mTempVisibleRect = new Rect(); + } + final Rect tempVisibleRect = mTempVisibleRect; + if (!mView.getLocalVisibleRect(tempVisibleRect)) { return false; } // Check if the view intersects the visible portion of the parent. - return localRect.intersect(mTempVisibleRect); + return localRect.intersect(tempVisibleRect); } /** @@ -583,7 +613,7 @@ public abstract class ExploreByTouchHelper extends View.AccessibilityDelegate { * * @param virtualViewIds The list to populate with visible items */ - protected abstract void getVisibleVirtualViews(List<Integer> virtualViewIds); + protected abstract void getVisibleVirtualViews(IntArray virtualViewIds); /** * Populates an {@link AccessibilityEvent} with information about the diff --git a/core/java/com/android/internal/widget/ILockSettings.aidl b/core/java/com/android/internal/widget/ILockSettings.aidl index c70841b..9501f92 100644 --- a/core/java/com/android/internal/widget/ILockSettings.aidl +++ b/core/java/com/android/internal/widget/ILockSettings.aidl @@ -16,8 +16,6 @@ package com.android.internal.widget; -import com.android.internal.widget.ILockSettingsObserver; - /** {@hide} */ interface ILockSettings { void setBoolean(in String key, in boolean value, in int userId); @@ -34,6 +32,4 @@ interface ILockSettings { boolean havePattern(int userId); boolean havePassword(int userId); void removeUser(int userId); - void registerObserver(in ILockSettingsObserver observer); - void unregisterObserver(in ILockSettingsObserver observer); } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 3326e42..0afc651 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -194,9 +194,6 @@ public class LockPatternUtils { return trust; } - /** - * @param contentResolver Used to look up and save settings. - */ public LockPatternUtils(Context context) { mContext = context; mContentResolver = context.getContentResolver(); @@ -210,8 +207,9 @@ public class LockPatternUtils { private ILockSettings getLockSettings() { if (mLockSettingsService == null) { - mLockSettingsService = LockPatternUtilsCache.getInstance( - ILockSettings.Stub.asInterface(ServiceManager.getService("lock_settings"))); + ILockSettings service = ILockSettings.Stub.asInterface( + ServiceManager.getService("lock_settings")); + mLockSettingsService = service; } return mLockSettingsService; } @@ -386,8 +384,16 @@ public class LockPatternUtils { * @return Whether a saved pattern exists. */ public boolean savedPatternExists() { + return savedPatternExists(getCurrentOrCallingUserId()); + } + + /** + * Check to see if the user has stored a lock pattern. + * @return Whether a saved pattern exists. + */ + public boolean savedPatternExists(int userId) { try { - return getLockSettings().havePattern(getCurrentOrCallingUserId()); + return getLockSettings().havePattern(userId); } catch (RemoteException re) { return false; } @@ -398,8 +404,16 @@ public class LockPatternUtils { * @return Whether a saved pattern exists. */ public boolean savedPasswordExists() { + return savedPasswordExists(getCurrentOrCallingUserId()); + } + + /** + * Check to see if the user has stored a lock pattern. + * @return Whether a saved pattern exists. + */ + public boolean savedPasswordExists(int userId) { try { - return getLockSettings().havePassword(getCurrentOrCallingUserId()); + return getLockSettings().havePassword(userId); } catch (RemoteException re) { return false; } @@ -476,17 +490,23 @@ public class LockPatternUtils { return activePasswordQuality; } + public void clearLock(boolean isFallback) { + clearLock(isFallback, getCurrentOrCallingUserId()); + } + /** * Clear any lock pattern or password. */ - public void clearLock(boolean isFallback) { - if(!isFallback) deleteGallery(); - saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); - setLockPatternEnabled(false); - saveLockPattern(null); - setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); - setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); - onAfterChangingPassword(); + public void clearLock(boolean isFallback, int userHandle) { + if(!isFallback) deleteGallery(userHandle); + saveLockPassword(null, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, isFallback, + userHandle); + setLockPatternEnabled(false, userHandle); + saveLockPattern(null, isFallback, userHandle); + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userHandle); + setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, + userHandle); + onAfterChangingPassword(userHandle); } /** @@ -533,11 +553,11 @@ public class LockPatternUtils { /** * Calls back SetupFaceLock to delete the gallery file when the lock type is changed */ - void deleteGallery() { - if(usingBiometricWeak()) { + void deleteGallery(int userId) { + if(usingBiometricWeak(userId)) { Intent intent = new Intent().setAction("com.android.facelock.DELETE_GALLERY"); intent.putExtra("deleteGallery", true); - mContext.sendBroadcast(intent); + mContext.sendBroadcastAsUser(intent, new UserHandle(userId)); } } @@ -552,11 +572,20 @@ public class LockPatternUtils { /** * Save a lock pattern. * @param pattern The new pattern to save. - * @param isFallback Specifies if this is a fallback to biometric weak */ public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback) { + this.saveLockPattern(pattern, isFallback, getCurrentOrCallingUserId()); + } + + /** + * Save a lock pattern. + * @param pattern The new pattern to save. + * @param isFallback Specifies if this is a fallback to biometric weak + * @param userId the user whose pattern is to be saved. + */ + public void saveLockPattern(List<LockPatternView.Cell> pattern, boolean isFallback, + int userId) { try { - int userId = getCurrentOrCallingUserId(); getLockSettings().setLockPattern(patternToString(pattern), userId); DevicePolicyManager dpm = getDevicePolicyManager(); if (pattern != null) { @@ -572,17 +601,17 @@ public class LockPatternUtils { } } - setBoolean(PATTERN_EVER_CHOSEN_KEY, true); + setBoolean(PATTERN_EVER_CHOSEN_KEY, true, userId); if (!isFallback) { - deleteGallery(); - setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); + deleteGallery(userId); + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId); dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, pattern.size(), 0, 0, 0, 0, 0, 0, userId); } else { - setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK); + setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, userId); setLong(PASSWORD_TYPE_ALTERNATE_KEY, - DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); - finishBiometricWeak(); + DevicePolicyManager.PASSWORD_QUALITY_SOMETHING, userId); + finishBiometricWeak(userId); dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, 0, 0, 0, 0, 0, 0, 0, userId); } @@ -590,7 +619,7 @@ public class LockPatternUtils { dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userId); } - onAfterChangingPassword(); + onAfterChangingPassword(userId); } catch (RemoteException re) { Log.e(TAG, "Couldn't save lock pattern " + re); } @@ -808,7 +837,7 @@ public class LockPatternUtils { } if (!isFallback) { - deleteGallery(); + deleteGallery(userHandle); setLong(PASSWORD_TYPE_KEY, Math.max(quality, computedQuality), userHandle); if (computedQuality != DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) { int letters = 0; @@ -848,7 +877,7 @@ public class LockPatternUtils { userHandle); setLong(PASSWORD_TYPE_ALTERNATE_KEY, Math.max(quality, computedQuality), userHandle); - finishBiometricWeak(); + finishBiometricWeak(userHandle); dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK, 0, 0, 0, 0, 0, 0, 0, userHandle); } @@ -856,7 +885,7 @@ public class LockPatternUtils { // password hashes have the same length for simplicity of implementation. String passwordHistory = getString(PASSWORD_HISTORY_KEY, userHandle); if (passwordHistory == null) { - passwordHistory = new String(); + passwordHistory = ""; } int passwordHistoryLength = getRequestedPasswordHistoryLength(); if (passwordHistoryLength == 0) { @@ -883,7 +912,7 @@ public class LockPatternUtils { DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userHandle); } - onAfterChangingPassword(); + onAfterChangingPassword(userHandle); } catch (RemoteException re) { // Cant do much Log.e(TAG, "Unable to save lock password " + re); @@ -957,8 +986,15 @@ public class LockPatternUtils { * @return true if the lockscreen method is set to biometric weak */ public boolean usingBiometricWeak() { - int quality = - (int) getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); + return usingBiometricWeak(getCurrentOrCallingUserId()); + } + + /** + * @return true if the lockscreen method is set to biometric weak + */ + public boolean usingBiometricWeak(int userId) { + int quality = (int) getLong( + PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId); return quality == DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK; } @@ -1094,15 +1130,22 @@ public class LockPatternUtils { * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak */ public boolean isLockPatternEnabled() { + return isLockPatternEnabled(getCurrentOrCallingUserId()); + } + + /** + * @return Whether the lock pattern is enabled, or if it is set as a backup for biometric weak + */ + public boolean isLockPatternEnabled(int userId) { final boolean backupEnabled = getLong(PASSWORD_TYPE_ALTERNATE_KEY, - DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) + DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, userId) == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; - return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false) - && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED) - == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING || - (usingBiometricWeak() && backupEnabled)); + return getBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, false, userId) + && (getLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, + userId) == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING + || (usingBiometricWeak(userId) && backupEnabled)); } /** @@ -1158,7 +1201,14 @@ public class LockPatternUtils { * Set whether the lock pattern is enabled. */ public void setLockPatternEnabled(boolean enabled) { - setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled); + setLockPatternEnabled(enabled, getCurrentOrCallingUserId()); + } + + /** + * Set whether the lock pattern is enabled. + */ + public void setLockPatternEnabled(boolean enabled, int userHandle) { + setBoolean(Settings.Secure.LOCK_PATTERN_ENABLED, enabled, userHandle); } /** @@ -1483,15 +1533,20 @@ public class LockPatternUtils { } public boolean isSecure() { - long mode = getKeyguardStoredPasswordQuality(); + return isSecure(getCurrentOrCallingUserId()); + } + + public boolean isSecure(int userId) { + long mode = getKeyguardStoredPasswordQuality(userId); final boolean isPattern = mode == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; final boolean isPassword = mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC || mode == DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC || mode == DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC || mode == DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; - final boolean secure = isPattern && isLockPatternEnabled() && savedPatternExists() - || isPassword && savedPasswordExists(); + final boolean secure = + isPattern && isLockPatternEnabled(userId) && savedPatternExists(userId) + || isPassword && savedPasswordExists(userId); return secure; } @@ -1547,15 +1602,15 @@ public class LockPatternUtils { return (TelecomManager) mContext.getSystemService(Context.TELECOM_SERVICE); } - private void finishBiometricWeak() { - setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true); + private void finishBiometricWeak(int userId) { + setBoolean(BIOMETRIC_WEAK_EVER_CHOSEN_KEY, true, userId); // Launch intent to show final screen, this also // moves the temporary gallery to the actual gallery Intent intent = new Intent(); intent.setClassName("com.android.facelock", "com.android.facelock.SetupEndScreen"); - mContext.startActivity(intent); + mContext.startActivityAsUser(intent, new UserHandle(userId)); } public void setPowerButtonInstantlyLocks(boolean enabled) { @@ -1649,8 +1704,8 @@ public class LockPatternUtils { getTrustManager().reportRequireCredentialEntry(userId); } - private void onAfterChangingPassword() { - getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId()); + private void onAfterChangingPassword(int userHandle) { + getTrustManager().reportEnabledTrustAgentsChanged(userHandle); } public boolean isCredentialRequiredToDecrypt(boolean defaultValue) { diff --git a/core/java/com/android/internal/widget/LockPatternUtilsCache.java b/core/java/com/android/internal/widget/LockPatternUtilsCache.java deleted file mode 100644 index 624f67c..0000000 --- a/core/java/com/android/internal/widget/LockPatternUtilsCache.java +++ /dev/null @@ -1,243 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -package com.android.internal.widget; - -import android.os.IBinder; -import android.os.RemoteException; -import android.util.ArrayMap; - -/** - * A decorator for {@link ILockSettings} that caches the key-value responses in memory. - * - * Specifically, the return values of {@link #getString(String, String, int)}, - * {@link #getLong(String, long, int)} and {@link #getBoolean(String, boolean, int)} are cached. - */ -public class LockPatternUtilsCache implements ILockSettings { - - private static final String HAS_LOCK_PATTERN_CACHE_KEY - = "LockPatternUtils.Cache.HasLockPatternCacheKey"; - private static final String HAS_LOCK_PASSWORD_CACHE_KEY - = "LockPatternUtils.Cache.HasLockPasswordCacheKey"; - - private static LockPatternUtilsCache sInstance; - - private final ILockSettings mService; - - /** Only access when holding {@code mCache} lock. */ - private final ArrayMap<CacheKey, Object> mCache = new ArrayMap<>(); - - /** Only access when holding {@link #mCache} lock. */ - private final CacheKey mCacheKey = new CacheKey(); - - - public static synchronized LockPatternUtilsCache getInstance(ILockSettings service) { - if (sInstance == null) { - sInstance = new LockPatternUtilsCache(service); - } - return sInstance; - } - - // ILockSettings - - private LockPatternUtilsCache(ILockSettings service) { - mService = service; - try { - service.registerObserver(mObserver); - } catch (RemoteException e) { - // Not safe to do caching without the observer. System process has probably died - // anyway, so crashing here is fine. - throw new RuntimeException(e); - } - } - - public void setBoolean(String key, boolean value, int userId) throws RemoteException { - invalidateCache(key, userId); - mService.setBoolean(key, value, userId); - putCache(key, userId, value); - } - - public void setLong(String key, long value, int userId) throws RemoteException { - invalidateCache(key, userId); - mService.setLong(key, value, userId); - putCache(key, userId, value); - } - - public void setString(String key, String value, int userId) throws RemoteException { - invalidateCache(key, userId); - mService.setString(key, value, userId); - putCache(key, userId, value); - } - - public long getLong(String key, long defaultValue, int userId) throws RemoteException { - Object value = peekCache(key, userId); - if (value instanceof Long) { - return (long) value; - } - long result = mService.getLong(key, defaultValue, userId); - putCache(key, userId, result); - return result; - } - - public String getString(String key, String defaultValue, int userId) throws RemoteException { - Object value = peekCache(key, userId); - if (value instanceof String) { - return (String) value; - } - String result = mService.getString(key, defaultValue, userId); - putCache(key, userId, result); - return result; - } - - public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException { - Object value = peekCache(key, userId); - if (value instanceof Boolean) { - return (boolean) value; - } - boolean result = mService.getBoolean(key, defaultValue, userId); - putCache(key, userId, result); - return result; - } - - @Override - public void setLockPattern(String pattern, int userId) throws RemoteException { - invalidateCache(HAS_LOCK_PATTERN_CACHE_KEY, userId); - mService.setLockPattern(pattern, userId); - putCache(HAS_LOCK_PATTERN_CACHE_KEY, userId, pattern != null); - } - - @Override - public boolean checkPattern(String pattern, int userId) throws RemoteException { - return mService.checkPattern(pattern, userId); - } - - @Override - public void setLockPassword(String password, int userId) throws RemoteException { - invalidateCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId); - mService.setLockPassword(password, userId); - putCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId, password != null); - } - - @Override - public boolean checkPassword(String password, int userId) throws RemoteException { - return mService.checkPassword(password, userId); - } - - @Override - public boolean checkVoldPassword(int userId) throws RemoteException { - return mService.checkVoldPassword(userId); - } - - @Override - public boolean havePattern(int userId) throws RemoteException { - Object value = peekCache(HAS_LOCK_PATTERN_CACHE_KEY, userId); - if (value instanceof Boolean) { - return (boolean) value; - } - boolean result = mService.havePattern(userId); - putCache(HAS_LOCK_PATTERN_CACHE_KEY, userId, result); - return result; - } - - @Override - public boolean havePassword(int userId) throws RemoteException { - Object value = peekCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId); - if (value instanceof Boolean) { - return (boolean) value; - } - boolean result = mService.havePassword(userId); - putCache(HAS_LOCK_PASSWORD_CACHE_KEY, userId, result); - return result; - } - - @Override - public void removeUser(int userId) throws RemoteException { - mService.removeUser(userId); - } - - @Override - public void registerObserver(ILockSettingsObserver observer) throws RemoteException { - mService.registerObserver(observer); - } - - @Override - public void unregisterObserver(ILockSettingsObserver observer) throws RemoteException { - mService.unregisterObserver(observer); - } - - @Override - public IBinder asBinder() { - return mService.asBinder(); - } - - // Caching - - private Object peekCache(String key, int userId) { - synchronized (mCache) { - // Safe to reuse mCacheKey, because it is not stored in the map. - return mCache.get(mCacheKey.set(key, userId)); - } - } - - private void putCache(String key, int userId, Object value) { - synchronized (mCache) { - // Create a new key, because this will be stored in the map. - mCache.put(new CacheKey().set(key, userId), value); - } - } - - private void invalidateCache(String key, int userId) { - synchronized (mCache) { - // Safe to reuse mCacheKey, because it is not stored in the map. - mCache.remove(mCacheKey.set(key, userId)); - } - } - - private final ILockSettingsObserver mObserver = new ILockSettingsObserver.Stub() { - @Override - public void onLockSettingChanged(String key, int userId) throws RemoteException { - invalidateCache(key, userId); - } - }; - - private static final class CacheKey { - String key; - int userId; - - public CacheKey set(String key, int userId) { - this.key = key; - this.userId = userId; - return this; - } - - public CacheKey copy() { - return new CacheKey().set(key, userId); - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof CacheKey)) - return false; - CacheKey o = (CacheKey) obj; - return userId == o.userId && key.equals(o.key); - } - - @Override - public int hashCode() { - return key.hashCode() ^ userId; - } - } -} diff --git a/core/java/com/android/internal/widget/ResolverDrawerLayout.java b/core/java/com/android/internal/widget/ResolverDrawerLayout.java index 375822f..4e48454 100644 --- a/core/java/com/android/internal/widget/ResolverDrawerLayout.java +++ b/core/java/com/android/internal/widget/ResolverDrawerLayout.java @@ -20,6 +20,7 @@ package com.android.internal.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Rect; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.util.AttributeSet; @@ -31,6 +32,8 @@ import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; import android.view.ViewTreeObserver; +import android.view.accessibility.AccessibilityEvent; +import android.view.accessibility.AccessibilityNodeInfo; import android.view.animation.AnimationUtils; import android.widget.AbsListView; import android.widget.OverScroller; @@ -63,18 +66,22 @@ public class ResolverDrawerLayout extends ViewGroup { private float mCollapseOffset; private int mCollapsibleHeight; + private int mUncollapsibleHeight; private int mTopOffset; private boolean mIsDragging; private boolean mOpenOnClick; private boolean mOpenOnLayout; + private boolean mDismissOnScrollerFinished; private final int mTouchSlop; private final float mMinFlingVelocity; private final OverScroller mScroller; private final VelocityTracker mVelocityTracker; - private OnClickListener mClickOutsideListener; + private OnDismissedListener mOnDismissedListener; + private RunOnDismissedListener mRunOnDismissedListener; + private float mInitialTouchX; private float mInitialTouchY; private float mLastTouchY; @@ -143,8 +150,8 @@ public class ResolverDrawerLayout extends ViewGroup { return isSmallCollapsed() ? mMaxCollapsedHeightSmall : mMaxCollapsedHeight; } - public void setOnClickOutsideListener(OnClickListener listener) { - mClickOutsideListener = listener; + public void setOnDismissedListener(OnDismissedListener listener) { + mOnDismissedListener = listener; } @Override @@ -194,7 +201,7 @@ public class ResolverDrawerLayout extends ViewGroup { } if (mIsDragging) { - mScroller.abortAnimation(); + abortAnimation(); } return mIsDragging || mOpenOnClick; } @@ -213,12 +220,10 @@ public class ResolverDrawerLayout extends ViewGroup { mInitialTouchX = x; mInitialTouchY = mLastTouchY = y; mActivePointerId = ev.getPointerId(0); - if (findChildUnder(mInitialTouchX, mInitialTouchY) == null && - mClickOutsideListener != null) { - mIsDragging = handled = true; - } - handled |= mCollapsibleHeight > 0; - mScroller.abortAnimation(); + final boolean hitView = findChildUnder(mInitialTouchX, mInitialTouchY) != null; + handled = (!hitView && mOnDismissedListener != null) || mCollapsibleHeight > 0; + mIsDragging = hitView && handled; + abortAnimation(); } break; @@ -264,11 +269,12 @@ public class ResolverDrawerLayout extends ViewGroup { break; case MotionEvent.ACTION_UP: { + final boolean wasDragging = mIsDragging; mIsDragging = false; - if (!mIsDragging && findChildUnder(mInitialTouchX, mInitialTouchY) == null && + if (!wasDragging && findChildUnder(mInitialTouchX, mInitialTouchY) == null && findChildUnder(ev.getX(), ev.getY()) == null) { - if (mClickOutsideListener != null) { - mClickOutsideListener.onClick(this); + if (mOnDismissedListener != null) { + dispatchOnDismissed(); resetTouch(); return true; } @@ -281,7 +287,13 @@ public class ResolverDrawerLayout extends ViewGroup { mVelocityTracker.computeCurrentVelocity(1000); final float yvel = mVelocityTracker.getYVelocity(mActivePointerId); if (Math.abs(yvel) > mMinFlingVelocity) { - smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel); + if (mOnDismissedListener != null + && yvel > 0 && mCollapseOffset > mCollapsibleHeight) { + smoothScrollTo(mCollapsibleHeight + mUncollapsibleHeight, yvel); + mDismissOnScrollerFinished = true; + } else { + smoothScrollTo(yvel < 0 ? 0 : mCollapsibleHeight, yvel); + } } else { smoothScrollTo( mCollapseOffset < mCollapsibleHeight / 2 ? 0 : mCollapsibleHeight, 0); @@ -327,17 +339,27 @@ public class ResolverDrawerLayout extends ViewGroup { @Override public void computeScroll() { super.computeScroll(); - if (!mScroller.isFinished()) { - final boolean keepGoing = mScroller.computeScrollOffset(); + if (mScroller.computeScrollOffset()) { + final boolean keepGoing = !mScroller.isFinished(); performDrag(mScroller.getCurrY() - mCollapseOffset); if (keepGoing) { postInvalidateOnAnimation(); + } else if (mDismissOnScrollerFinished && mOnDismissedListener != null) { + mRunOnDismissedListener = new RunOnDismissedListener(); + post(mRunOnDismissedListener); } } } + private void abortAnimation() { + mScroller.abortAnimation(); + mRunOnDismissedListener = null; + mDismissOnScrollerFinished = false; + } + private float performDrag(float dy) { - final float newPos = Math.max(0, Math.min(mCollapseOffset + dy, mCollapsibleHeight)); + final float newPos = Math.max(0, Math.min(mCollapseOffset + dy, + mCollapsibleHeight + mUncollapsibleHeight)); if (newPos != mCollapseOffset) { dy = newPos - mCollapseOffset; final int childCount = getChildCount(); @@ -348,19 +370,32 @@ public class ResolverDrawerLayout extends ViewGroup { child.offsetTopAndBottom((int) dy); } } + final boolean isCollapsedOld = mCollapseOffset != 0; mCollapseOffset = newPos; mTopOffset += dy; + final boolean isCollapsedNew = newPos != 0; + if (isCollapsedOld != isCollapsedNew) { + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } postInvalidateOnAnimation(); return dy; } return 0; } - private void smoothScrollTo(int yOffset, float velocity) { - if (getMaxCollapsedHeight() == 0) { - return; + void dispatchOnDismissed() { + if (mOnDismissedListener != null) { + mOnDismissedListener.onDismissed(); } - mScroller.abortAnimation(); + if (mRunOnDismissedListener != null) { + removeCallbacks(mRunOnDismissedListener); + mRunOnDismissedListener = null; + } + } + + private void smoothScrollTo(int yOffset, float velocity) { + abortAnimation(); final int sy = (int) mCollapseOffset; int dy = yOffset - sy; if (dy == 0) { @@ -490,6 +525,7 @@ public class ResolverDrawerLayout extends ViewGroup { protected void onDetachedFromWindow() { super.onDetachedFromWindow(); getViewTreeObserver().removeOnTouchModeChangeListener(mTouchModeChangeListener); + abortAnimation(); } @Override @@ -544,6 +580,50 @@ public class ResolverDrawerLayout extends ViewGroup { } @Override + public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle args) { + if (super.onNestedPrePerformAccessibilityAction(target, action, args)) { + return true; + } + + if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD && mCollapseOffset != 0) { + smoothScrollTo(0, 0); + return true; + } + return false; + } + + @Override + public void onInitializeAccessibilityEvent(AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(event); + event.setClassName(ResolverDrawerLayout.class.getName()); + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName(ResolverDrawerLayout.class.getName()); + if (isEnabled()) { + if (mCollapseOffset != 0) { + info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD); + info.setScrollable(true); + } + } + } + + @Override + public boolean performAccessibilityAction(int action, Bundle arguments) { + if (super.performAccessibilityAction(action, arguments)) { + return true; + } + + if (action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD && mCollapseOffset != 0) { + smoothScrollTo(0, 0); + return true; + } + return false; + } + + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int sourceWidth = MeasureSpec.getSize(widthMeasureSpec); int widthSize = sourceWidth; @@ -585,9 +665,16 @@ public class ResolverDrawerLayout extends ViewGroup { mCollapsibleHeight = Math.max(0, heightUsed - alwaysShowHeight - getMaxCollapsedHeight()); + mUncollapsibleHeight = heightUsed - mCollapsibleHeight; if (isLaidOut()) { + final boolean isCollapsedOld = mCollapseOffset != 0; mCollapseOffset = Math.min(mCollapseOffset, mCollapsibleHeight); + final boolean isCollapsedNew = mCollapseOffset != 0; + if (isCollapsedOld != isCollapsedNew) { + notifyViewAccessibilityStateChangedIfNeeded( + AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED); + } } else { // Start out collapsed at first unless we restored state for otherwise mCollapseOffset = mOpenOnLayout ? 0 : mCollapsibleHeight; @@ -734,4 +821,15 @@ public class ResolverDrawerLayout extends ViewGroup { } }; } + + public interface OnDismissedListener { + public void onDismissed(); + } + + private class RunOnDismissedListener implements Runnable { + @Override + public void run() { + dispatchOnDismissed(); + } + } } diff --git a/core/java/com/android/internal/widget/SwipeDismissLayout.java b/core/java/com/android/internal/widget/SwipeDismissLayout.java index 97b1634..d617c05 100644 --- a/core/java/com/android/internal/widget/SwipeDismissLayout.java +++ b/core/java/com/android/internal/widget/SwipeDismissLayout.java @@ -17,6 +17,7 @@ package com.android.internal.widget; import android.animation.TimeInterpolator; +import android.app.Activity; import android.content.Context; import android.util.AttributeSet; import android.util.Log; @@ -25,6 +26,7 @@ import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import android.widget.FrameLayout; @@ -75,6 +77,19 @@ public class SwipeDismissLayout extends FrameLayout { private OnDismissedListener mDismissedListener; private OnSwipeProgressChangedListener mProgressListener; + private ViewTreeObserver.OnEnterAnimationCompleteListener mOnEnterAnimationCompleteListener = + new ViewTreeObserver.OnEnterAnimationCompleteListener() { + @Override + public void onEnterAnimationComplete() { + // SwipeDismissLayout assumes that the host Activity is translucent + // and temporarily disables translucency when it is fully visible. + // As soon as the user starts swiping, we will re-enable + // translucency. + if (getContext() instanceof Activity) { + ((Activity) getContext()).convertFromTranslucent(); + } + } + }; private float mLastX; @@ -113,6 +128,24 @@ public class SwipeDismissLayout extends FrameLayout { } @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (getContext() instanceof Activity) { + getViewTreeObserver().addOnEnterAnimationCompleteListener( + mOnEnterAnimationCompleteListener); + } + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (getContext() instanceof Activity) { + getViewTreeObserver().removeOnEnterAnimationCompleteListener( + mOnEnterAnimationCompleteListener); + } + } + + @Override public boolean onInterceptTouchEvent(MotionEvent ev) { // offset because the view is translated during swipe ev.offsetLocation(mTranslationX, 0); @@ -197,6 +230,9 @@ public class SwipeDismissLayout extends FrameLayout { mLastX = ev.getRawX(); updateSwiping(ev); if (mSwiping) { + if (getContext() instanceof Activity) { + ((Activity) getContext()).convertToTranslucent(null, null); + } setProgress(ev.getRawX() - mDownX); break; } @@ -218,6 +254,9 @@ public class SwipeDismissLayout extends FrameLayout { } protected void cancel() { + if (getContext() instanceof Activity) { + ((Activity) getContext()).convertFromTranslucent(); + } if (mProgressListener != null) { mProgressListener.onSwipeCancelled(this); } diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java index 324a6c9..8d1f73a 100644 --- a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java +++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java @@ -97,6 +97,7 @@ public class ToolbarWidgetWrapper implements DecorToolbar { mTitle = toolbar.getTitle(); mSubtitle = toolbar.getSubtitle(); mTitleSet = mTitle != null; + mNavIcon = mToolbar.getNavigationIcon(); final TypedArray a = toolbar.getContext().obtainStyledAttributes(null, R.styleable.ActionBar, R.attr.actionBarStyle, 0); mDefaultNavigationIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator); @@ -120,7 +121,7 @@ public class ToolbarWidgetWrapper implements DecorToolbar { if (icon != null) { setIcon(icon); } - if (mDefaultNavigationIcon != null) { + if (mNavIcon == null && mDefaultNavigationIcon != null) { setNavigationIcon(mDefaultNavigationIcon); } setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0)); @@ -657,4 +658,36 @@ public class ToolbarWidgetWrapper implements DecorToolbar { mToolbar.restoreHierarchyState(toolbarStates); } + @Override + public void setBackgroundDrawable(Drawable d) { + //noinspection deprecation + mToolbar.setBackgroundDrawable(d); + } + + @Override + public int getHeight() { + return mToolbar.getHeight(); + } + + @Override + public void setVisibility(int visible) { + mToolbar.setVisibility(visible); + } + + @Override + public int getVisibility() { + return mToolbar.getVisibility(); + } + + @Override + public void setMenuCallbacks(MenuPresenter.Callback presenterCallback, + MenuBuilder.Callback menuBuilderCallback) { + mToolbar.setMenuCallbacks(presenterCallback, menuBuilderCallback); + } + + @Override + public Menu getMenu() { + return mToolbar.getMenu(); + } + } diff --git a/core/java/com/android/server/BootReceiver.java b/core/java/com/android/server/BootReceiver.java index d39bf07..155f5d3 100644 --- a/core/java/com/android/server/BootReceiver.java +++ b/core/java/com/android/server/BootReceiver.java @@ -142,6 +142,8 @@ public class BootReceiver extends BroadcastReceiver { "SYSTEM_LAST_KMSG"); addFileToDropBox(db, prefs, headers, "/cache/recovery/log", -LOG_SIZE, "SYSTEM_RECOVERY_LOG"); + addFileToDropBox(db, prefs, headers, "/cache/recovery/last_kmsg", + -LOG_SIZE, "SYSTEM_RECOVERY_KMSG"); addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_console", -LOG_SIZE, "APANIC_CONSOLE"); addFileToDropBox(db, prefs, headers, "/data/dontpanic/apanic_threads", diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java index 26e2e2a..35a1a5a 100644 --- a/core/java/com/android/server/backup/SystemBackupAgent.java +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -17,12 +17,14 @@ package com.android.server.backup; +import android.app.ActivityManagerNative; import android.app.IWallpaperManager; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; import android.app.backup.BackupAgentHelper; import android.app.backup.FullBackup; import android.app.backup.FullBackupDataOutput; +import android.app.backup.RecentsBackupHelper; import android.app.backup.WallpaperBackupHelper; import android.content.Context; import android.os.Environment; @@ -83,6 +85,8 @@ public class SystemBackupAgent extends BackupAgentHelper { } } addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files, keys)); + addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this)); + super.onBackup(oldState, data, newState); } @@ -113,6 +117,7 @@ public class SystemBackupAgent extends BackupAgentHelper { addHelper("system_files", new WallpaperBackupHelper(SystemBackupAgent.this, new String[] { WALLPAPER_IMAGE }, new String[] { WALLPAPER_IMAGE_KEY} )); + addHelper("recents", new RecentsBackupHelper(SystemBackupAgent.this)); try { super.onRestore(data, appVersionCode, newState); @@ -182,4 +187,13 @@ public class SystemBackupAgent extends BackupAgentHelper { } } } + + @Override + public void onRestoreFinished() { + try { + ActivityManagerNative.getDefault().systemBackupRestored(); + } catch (RemoteException e) { + // Not possible since this code is running in the system process. + } + } } |