diff options
Diffstat (limited to 'core/java/android')
116 files changed, 2930 insertions, 2382 deletions
diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index de01153..d5a9b02 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -38,6 +38,7 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.os.Binder; import android.os.Bundle; +import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -52,6 +53,7 @@ import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import java.io.File; import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -153,8 +155,7 @@ public class AccountManagerService new AtomicReference<AccountManagerService>(); private static final boolean isDebuggableMonkeyBuild = - SystemProperties.getBoolean("ro.monkey", false) - && SystemProperties.getBoolean("ro.debuggable", false); + SystemProperties.getBoolean("ro.monkey", false); private static final Account[] EMPTY_ACCOUNT_ARRAY = new Account[]{}; static { @@ -1351,9 +1352,20 @@ public class AccountManagerService } } + private static String getDatabaseName() { + if(Environment.isEncryptedFilesystemEnabled()) { + // Hard-coded path in case of encrypted file system + return Environment.getSystemSecureDirectory().getPath() + File.separator + DATABASE_NAME; + } else { + // Regular path in case of non-encrypted file system + return DATABASE_NAME; + } + } + private class DatabaseHelper extends SQLiteOpenHelper { + public DatabaseHelper(Context context) { - super(context, DATABASE_NAME, null, DATABASE_VERSION); + super(context, AccountManagerService.getDatabaseName(), null, DATABASE_VERSION); } @Override diff --git a/core/java/android/annotation/SdkConstant.java b/core/java/android/annotation/SdkConstant.java index 6ac70f0..0a53186 100644 --- a/core/java/android/annotation/SdkConstant.java +++ b/core/java/android/annotation/SdkConstant.java @@ -29,7 +29,7 @@ import java.lang.annotation.RetentionPolicy; @Retention(RetentionPolicy.SOURCE) public @interface SdkConstant { public static enum SdkConstantType { - ACTIVITY_INTENT_ACTION, BROADCAST_INTENT_ACTION, SERVICE_ACTION, INTENT_CATEGORY; + ACTIVITY_INTENT_ACTION, BROADCAST_INTENT_ACTION, SERVICE_ACTION, INTENT_CATEGORY, FEATURE; } SdkConstantType value(); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 49ebce3..1c3414d 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -2087,8 +2087,8 @@ public class Activity extends ContextThemeWrapper event.setPackageName(getPackageName()); LayoutParams params = getWindow().getAttributes(); - boolean isFullScreen = (params.width == LayoutParams.FILL_PARENT) && - (params.height == LayoutParams.FILL_PARENT); + boolean isFullScreen = (params.width == LayoutParams.MATCH_PARENT) && + (params.height == LayoutParams.MATCH_PARENT); event.setFullScreen(isFullScreen); CharSequence title = getTitle(); diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 676d6d5..932ad53 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -606,7 +606,7 @@ public class ActivityManager { public int uid; /** - * The tag that was provided when the process crashed. + * The activity name associated with the error, if known. May be null. */ public String tag; @@ -622,7 +622,6 @@ public class ActivityManager { /** * The stack trace where the error originated. May be null. - * @pending */ public String stackTrace; @@ -891,6 +890,38 @@ public class ActivityManager { } /** + * @deprecated This is now just a wrapper for + * {@link #killBackgroundProcesses(String)}; the previous behavior here + * is no longer available to applications because it allows them to + * break other applications by removing their alarms, stopping their + * services, etc. + */ + @Deprecated + public void restartPackage(String packageName) { + killBackgroundProcesses(packageName); + } + + /** + * Have the system immediately kill all background processes associated + * with the given package. This is the same as the kernel killing those + * processes to reclaim memory; the system will take care of restarting + * these processes in the future as needed. + * + * <p>You must hold the permission + * {@link android.Manifest.permission#KILL_BACKGROUND_PROCESSES} to be able to + * call this method. + * + * @param packageName The name of the package whose processes are to + * be killed. + */ + public void killBackgroundProcesses(String packageName) { + try { + ActivityManagerNative.getDefault().killBackgroundProcesses(packageName); + } catch (RemoteException e) { + } + } + + /** * Have the system perform a force stop of everything associated with * the given application package. All processes that share its uid * will be killed, all services it has running stopped, all activities @@ -899,14 +930,18 @@ public class ActivityManager { * be stopped, notifications removed, etc. * * <p>You must hold the permission - * {@link android.Manifest.permission#RESTART_PACKAGES} to be able to + * {@link android.Manifest.permission#FORCE_STOP_PACKAGES} to be able to * call this method. * * @param packageName The name of the package to be stopped. + * + * @hide This is not available to third party applications due to + * it allowing them to break other applications by stopping their + * services, removing their alarms, etc. */ - public void restartPackage(String packageName) { + public void forceStopPackage(String packageName) { try { - ActivityManagerNative.getDefault().restartPackage(packageName); + ActivityManagerNative.getDefault().forceStopPackage(packageName); } catch (RemoteException e) { } } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index a0498aa..09b88ee 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -979,13 +979,23 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case HANDLE_APPLICATION_ERROR_TRANSACTION: { + case HANDLE_APPLICATION_CRASH_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + IBinder app = data.readStrongBinder(); + ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data); + handleApplicationCrash(app, ci); + reply.writeNoException(); + return true; + } + + case HANDLE_APPLICATION_WTF_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder app = data.readStrongBinder(); String tag = data.readString(); ApplicationErrorReport.CrashInfo ci = new ApplicationErrorReport.CrashInfo(data); - handleApplicationError(app, tag, ci); + boolean res = handleApplicationWtf(app, tag, ci); reply.writeNoException(); + reply.writeInt(res ? 1 : 0); return true; } @@ -997,10 +1007,18 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case RESTART_PACKAGE_TRANSACTION: { - data.enforceInterface(IActivityManager.descriptor); + case KILL_BACKGROUND_PROCESSES_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); String packageName = data.readString(); - restartPackage(packageName); + killBackgroundProcesses(packageName); + reply.writeNoException(); + return true; + } + + case FORCE_STOP_PACKAGE_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String packageName = data.readString(); + forceStopPackage(packageName); reply.writeNoException(); return true; } @@ -2337,7 +2355,20 @@ class ActivityManagerProxy implements IActivityManager /* this base class version is never called */ return true; } - public void handleApplicationError(IBinder app, String tag, + public void handleApplicationCrash(IBinder app, + ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeStrongBinder(app); + crashInfo.writeToParcel(data, 0); + mRemote.transact(HANDLE_APPLICATION_CRASH_TRANSACTION, data, reply, 0); + reply.readException(); + reply.recycle(); + data.recycle(); + } + public boolean handleApplicationWtf(IBinder app, String tag, ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException { Parcel data = Parcel.obtain(); @@ -2346,10 +2377,12 @@ class ActivityManagerProxy implements IActivityManager data.writeStrongBinder(app); data.writeString(tag); crashInfo.writeToParcel(data, 0); - mRemote.transact(HANDLE_APPLICATION_ERROR_TRANSACTION, data, reply, 0); + mRemote.transact(HANDLE_APPLICATION_WTF_TRANSACTION, data, reply, 0); reply.readException(); + boolean res = reply.readInt() != 0; reply.recycle(); data.recycle(); + return res; } public void signalPersistentProcesses(int sig) throws RemoteException { @@ -2363,12 +2396,23 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } - public void restartPackage(String packageName) throws RemoteException { + public void killBackgroundProcesses(String packageName) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(packageName); + mRemote.transact(KILL_BACKGROUND_PROCESSES_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + + public void forceStopPackage(String packageName) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeString(packageName); - mRemote.transact(RESTART_PACKAGE_TRANSACTION, data, reply, 0); + mRemote.transact(FORCE_STOP_PACKAGE_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java index 20a579a..2603579 100644 --- a/core/java/android/app/AlertDialog.java +++ b/core/java/android/app/AlertDialog.java @@ -40,7 +40,7 @@ import com.android.internal.app.AlertController; * * <pre> * FrameLayout fl = (FrameLayout) findViewById(R.id.body); - * fl.add(myView, new LayoutParams(FILL_PARENT, WRAP_CONTENT)); + * fl.add(myView, new LayoutParams(MATCH_PARENT, WRAP_CONTENT)); * </pre> * * <p>The AlertDialog class takes care of automatically setting diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java index de370d2..d89b877 100644 --- a/core/java/android/app/ApplicationContext.java +++ b/core/java/android/app/ApplicationContext.java @@ -466,14 +466,7 @@ class ApplicationContext extends Context { @Override public SQLiteDatabase openOrCreateDatabase(String name, int mode, CursorFactory factory) { - File dir = getDatabasesDir(); - if (!dir.isDirectory() && dir.mkdir()) { - FileUtils.setPermissions(dir.getPath(), - FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, - -1, -1); - } - - File f = makeFilename(dir, name); + File f = validateFilePath(name, true); SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, factory); setFilePermissionsFromMode(f.getPath(), mode, 0); return db; @@ -482,7 +475,7 @@ class ApplicationContext extends Context { @Override public boolean deleteDatabase(String name) { try { - File f = makeFilename(getDatabasesDir(), name); + File f = validateFilePath(name, false); return f.delete(); } catch (Exception e) { } @@ -491,7 +484,7 @@ class ApplicationContext extends Context { @Override public File getDatabasePath(String name) { - return makeFilename(getDatabasesDir(), name); + return validateFilePath(name, false); } @Override @@ -1454,12 +1447,35 @@ class ApplicationContext extends Context { FileUtils.setPermissions(name, perms, -1, -1); } + private File validateFilePath(String name, boolean createDirectory) { + File dir; + File f; + + if (name.charAt(0) == File.separatorChar) { + String dirPath = name.substring(0, name.lastIndexOf(File.separatorChar)); + dir = new File(dirPath); + name = name.substring(name.lastIndexOf(File.separatorChar)); + f = new File(dir, name); + } else { + dir = getDatabasesDir(); + f = makeFilename(dir, name); + } + + if (createDirectory && !dir.isDirectory() && dir.mkdir()) { + FileUtils.setPermissions(dir.getPath(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, + -1, -1); + } + + return f; + } + private File makeFilename(File base, String name) { if (name.indexOf(File.separatorChar) < 0) { return new File(base, name); } throw new IllegalArgumentException( - "File " + name + " contains a path separator"); + "File " + name + " contains a path separator"); } // ---------------------------------------------------------------------- diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java index e89b3ad0..a4b692f 100644 --- a/core/java/android/app/ApplicationErrorReport.java +++ b/core/java/android/app/ApplicationErrorReport.java @@ -195,6 +195,7 @@ public class ApplicationErrorReport implements Parcelable { StringWriter sw = new StringWriter(); tr.printStackTrace(new PrintWriter(sw)); stackTrace = sw.toString(); + exceptionMessage = tr.getMessage(); // Populate fields with the "root cause" exception while (tr.getCause() != null) { diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 58e8b32..fa5d4a8 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -668,8 +668,8 @@ public class Dialog implements DialogInterface, Window.Callback, event.setPackageName(mContext.getPackageName()); LayoutParams params = getWindow().getAttributes(); - boolean isFullScreen = (params.width == LayoutParams.FILL_PARENT) && - (params.height == LayoutParams.FILL_PARENT); + boolean isFullScreen = (params.width == LayoutParams.MATCH_PARENT) && + (params.height == LayoutParams.MATCH_PARENT); event.setFullScreen(isFullScreen); return false; diff --git a/core/java/android/app/ExpandableListActivity.java b/core/java/android/app/ExpandableListActivity.java index a2e048f..9651078 100644 --- a/core/java/android/app/ExpandableListActivity.java +++ b/core/java/android/app/ExpandableListActivity.java @@ -65,21 +65,21 @@ import java.util.Map; * <?xml version="1.0" encoding="UTF-8"?> * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" * android:orientation="vertical" - * android:layout_width="fill_parent" - * android:layout_height="fill_parent" + * android:layout_width="match_parent" + * android:layout_height="match_parent" * android:paddingLeft="8dp" * android:paddingRight="8dp"> * * <ExpandableListView android:id="@id/android:list" - * android:layout_width="fill_parent" - * android:layout_height="fill_parent" + * android:layout_width="match_parent" + * android:layout_height="match_parent" * android:background="#00FF00" * android:layout_weight="1" * android:drawSelectorOnTop="false"/> * * <TextView android:id="@id/android:empty" - * android:layout_width="fill_parent" - * android:layout_height="fill_parent" + * android:layout_width="match_parent" + * android:layout_height="match_parent" * android:background="#FF0000" * android:text="No data"/> * </LinearLayout> @@ -114,19 +114,19 @@ import java.util.Map; * <pre> * <?xml version="1.0" encoding="utf-8"?> * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - * android:layout_width="fill_parent" + * android:layout_width="match_parent" * android:layout_height="wrap_content" * android:orientation="vertical"> * * <TextView android:id="@+id/text1" * android:textSize="16sp" * android:textStyle="bold" - * android:layout_width="fill_parent" + * android:layout_width="match_parent" * android:layout_height="wrap_content"/> * * <TextView android:id="@+id/text2" * android:textSize="16sp" - * android:layout_width="fill_parent" + * android:layout_width="match_parent" * android:layout_height="wrap_content"/> * </LinearLayout> * </pre> diff --git a/core/java/android/app/IActivityController.aidl b/core/java/android/app/IActivityController.aidl index 804dd61..c76a517 100644 --- a/core/java/android/app/IActivityController.aidl +++ b/core/java/android/app/IActivityController.aidl @@ -44,7 +44,7 @@ interface IActivityController * it immediately. */ boolean appCrashed(String processName, int pid, - String tag, String shortMsg, String longMsg, + String shortMsg, String longMsg, long timeMillis, String stackTrace); /** diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index c890c4c..016d465 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -216,7 +216,8 @@ public interface IActivityManager extends IInterface { public void getMemoryInfo(ActivityManager.MemoryInfo outInfo) throws RemoteException; - public void restartPackage(final String packageName) throws RemoteException; + public void killBackgroundProcesses(final String packageName) throws RemoteException; + public void forceStopPackage(final String packageName) throws RemoteException; // Note: probably don't want to allow applications access to these. public void goingToSleep() throws RemoteException; @@ -242,8 +243,9 @@ public interface IActivityManager extends IInterface { // Special low-level communication with activity manager. public void startRunning(String pkg, String cls, String action, String data) throws RemoteException; - - public void handleApplicationError(IBinder app, String tag, + public void handleApplicationCrash(IBinder app, + ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException; + public boolean handleApplicationWtf(IBinder app, String tag, ApplicationErrorReport.CrashInfo crashInfo) throws RemoteException; /* @@ -349,7 +351,7 @@ public interface IActivityManager extends IInterface { // Please keep these transaction codes the same -- they are also // sent by C++ code. int START_RUNNING_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION; - int HANDLE_APPLICATION_ERROR_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1; + int HANDLE_APPLICATION_CRASH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1; int START_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2; int UNHANDLED_BACK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3; int OPEN_CONTENT_URI_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4; @@ -423,7 +425,7 @@ public interface IActivityManager extends IInterface { int GET_MEMORY_INFO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+75; int GET_PROCESSES_IN_ERROR_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+76; int CLEAR_APP_DATA_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+77; - int RESTART_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78; + int FORCE_STOP_PACKAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+78; int KILL_PIDS_FOR_MEMORY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+79; int GET_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+80; int REPORT_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+81; @@ -446,4 +448,6 @@ public interface IActivityManager extends IInterface { int KILL_APPLICATION_PROCESS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+98; int START_ACTIVITY_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+99; int OVERRIDE_PENDING_TRANSITION_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+100; + int HANDLE_APPLICATION_WTF_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+101; + int KILL_BACKGROUND_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+102; } diff --git a/core/java/android/app/ListActivity.java b/core/java/android/app/ListActivity.java index 19b99c8..4b4cc05 100644 --- a/core/java/android/app/ListActivity.java +++ b/core/java/android/app/ListActivity.java @@ -56,21 +56,21 @@ import android.widget.ListView; * <?xml version="1.0" encoding="utf-8"?> * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" * android:orientation="vertical" - * android:layout_width="fill_parent" - * android:layout_height="fill_parent" + * android:layout_width="match_parent" + * android:layout_height="match_parent" * android:paddingLeft="8dp" * android:paddingRight="8dp"> * * <ListView android:id="@id/android:list" - * android:layout_width="fill_parent" - * android:layout_height="fill_parent" + * android:layout_width="match_parent" + * android:layout_height="match_parent" * android:background="#00FF00" * android:layout_weight="1" * android:drawSelectorOnTop="false"/> * * <TextView id="@id/android:empty" - * android:layout_width="fill_parent" - * android:layout_height="fill_parent" + * android:layout_width="match_parent" + * android:layout_height="match_parent" * android:background="#FF0000" * android:text="No data"/> * </LinearLayout> @@ -100,19 +100,19 @@ import android.widget.ListView; * <pre> * <?xml version="1.0" encoding="utf-8"?> * <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - * android:layout_width="fill_parent" + * android:layout_width="match_parent" * android:layout_height="wrap_content" * android:orientation="vertical"> * * <TextView android:id="@+id/text1" * android:textSize="16sp" * android:textStyle="bold" - * android:layout_width="fill_parent" + * android:layout_width="match_parent" * android:layout_height="wrap_content"/> * * <TextView android:id="@+id/text2" * android:textSize="16sp" - * android:layout_width="fill_parent" + * android:layout_width="match_parent" * android:layout_height="wrap_content"/> * </LinearLayout> * </pre> diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index b204c79..b396396 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -60,7 +60,6 @@ import android.widget.AdapterView; import android.widget.AutoCompleteTextView; import android.widget.Button; import android.widget.ImageButton; -import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; @@ -106,7 +105,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // views & widgets private TextView mBadgeLabel; - private ImageView mAppIcon; + private SearchSourceSelector mSourceSelector; private SearchAutoComplete mSearchAutoComplete; private Button mGoButton; private ImageButton mVoiceButton; @@ -182,11 +181,11 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS Window theWindow = getWindow(); WindowManager.LayoutParams lp = theWindow.getAttributes(); lp.type = WindowManager.LayoutParams.TYPE_SEARCH_BAR; - lp.width = ViewGroup.LayoutParams.FILL_PARENT; + lp.width = ViewGroup.LayoutParams.MATCH_PARENT; // taking up the whole window (even when transparent) is less than ideal, // but necessary to show the popup window until the window manager supports // having windows anchored by their parent but not clipped by them. - lp.height = ViewGroup.LayoutParams.FILL_PARENT; + lp.height = ViewGroup.LayoutParams.MATCH_PARENT; lp.gravity = Gravity.TOP | Gravity.FILL_HORIZONTAL; lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; theWindow.setAttributes(lp); @@ -209,7 +208,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge); mSearchAutoComplete = (SearchAutoComplete) findViewById(com.android.internal.R.id.search_src_text); - mAppIcon = (ImageView) findViewById(com.android.internal.R.id.search_app_icon); + mSourceSelector = new SearchSourceSelector( + findViewById(com.android.internal.R.id.search_source_selector)); mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn); mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn); mSearchPlate = findViewById(com.android.internal.R.id.search_plate); @@ -606,13 +606,16 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } private void updateSearchAppIcon() { + mSourceSelector.setSource(mSearchable.getSearchActivity()); + mSourceSelector.setAppSearchData(mAppSearchData); + // In Donut, we special-case the case of the browser to hide the app icon as if it were // global search, for extra space for url entry. // // TODO: Remove this special case once the issue has been reconciled in Eclair. if (mGlobalSearchMode || isBrowserSearch()) { - mAppIcon.setImageResource(0); - mAppIcon.setVisibility(View.GONE); + mSourceSelector.setSourceIcon(null); + mSourceSelector.setVisibility(View.GONE); mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL, mSearchPlate.getPaddingTop(), mSearchPlate.getPaddingRight(), @@ -628,8 +631,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS icon = pm.getDefaultActivityIcon(); Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon"); } - mAppIcon.setImageDrawable(icon); - mAppIcon.setVisibility(View.VISIBLE); + mSourceSelector.setSourceIcon(icon); + mSourceSelector.setVisibility(View.VISIBLE); mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL, mSearchPlate.getPaddingTop(), mSearchPlate.getPaddingRight(), @@ -812,6 +815,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (!mSearchAutoComplete.isPerformingCompletion()) { // The user changed the query, remember it. mUserQuery = s == null ? "" : s.toString(); + mSourceSelector.setQuery(mUserQuery); } } @@ -1927,6 +1931,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS query = ""; } mUserQuery = query; + mSourceSelector.setQuery(query); mSearchAutoComplete.setText(query); mSearchAutoComplete.setSelection(query.length()); } diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 5d9034b..a75e8dc 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -1346,6 +1346,7 @@ public class SearchManager * @hide Pending API council approval */ public final static String SELECT_INITIAL_QUERY = "select_initial_query"; + /** * Defines the constants used in the communication between {@link android.app.SearchDialog} and * the global search provider via {@link Cursor#respond(android.os.Bundle)}. @@ -1612,6 +1613,15 @@ public class SearchManager public final static String SUGGEST_PARAMETER_LIMIT = "limit"; /** + * Intent action for opening the search source selection activity. + * The intent may include these extra values: + * {@link #QUERY}, + * {@link #APP_DATA}. + */ + public static final String INTENT_ACTION_SELECT_SEARCH_SOURCE + = "android.intent.action.SELECT_SEARCH_SOURCE"; + + /** * If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION}, * the search dialog will switch to a different suggestion source when the * suggestion is clicked. @@ -2020,8 +2030,23 @@ public class SearchManager } /** - * Gets information about a searchable activity. This method is static so that it can - * be used from non-Activity contexts. + * Gets information about a searchable activity. + * + * @param componentName The activity to get searchable information for. + * @return Searchable information, or <code>null</code> if the activity does not + * exist, or is not searchable. + */ + public SearchableInfo getSearchableInfo(ComponentName componentName) { + try { + return mService.getSearchableInfo(componentName, false); + } catch (RemoteException ex) { + Log.e(TAG, "getSearchableInfo() failed: " + ex); + return null; + } + } + + /** + * Gets information about a searchable activity. * * @param componentName The activity to get searchable information for. * @param globalSearch If <code>false</code>, return information about the given activity. diff --git a/core/java/android/app/SearchSourceSelector.java b/core/java/android/app/SearchSourceSelector.java new file mode 100644 index 0000000..fabf858 --- /dev/null +++ b/core/java/android/app/SearchSourceSelector.java @@ -0,0 +1,197 @@ +/* + * 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 android.app; + +import com.android.internal.R; + +import android.content.ActivityNotFoundException; +import android.content.ComponentName; +import android.content.ContentResolver; +import android.content.Intent; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.net.Uri; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.View; +import android.widget.ImageButton; + +import java.util.List; + +/** + * Utilities for setting up the search source selector. + * + * This class has two copies: + * android.app.SearchSourceSelector + * com.android.quicksearchbox.ui.SearchSourceSelector + * + * They should keep the same look and feel as much as possible, + * but only the intent details must absolutely stay in sync. + * + * @hide + */ +public class SearchSourceSelector implements View.OnClickListener { + + private static final String TAG = "SearchSourceSelector"; + + // TODO: This should be defined in android.provider.Applications, + // and have a less made-up value. + private static final String APPLICATION_TYPE = "application/vnd.android.application"; + + public static final int ICON_VIEW_ID = R.id.search_source_selector_icon; + + private final View mView; + + private final ImageButton mIconView; + + private ComponentName mSource; + + private Bundle mAppSearchData; + + private String mQuery; + + public SearchSourceSelector(View view) { + mView = view; + mIconView = (ImageButton) view.findViewById(ICON_VIEW_ID); + mIconView.setOnClickListener(this); + } + + /** + * Sets the icon displayed in the search source selector. + */ + public void setSourceIcon(Drawable icon) { + mIconView.setImageDrawable(icon); + } + + /** + * Sets the current search source. + */ + public void setSource(ComponentName source) { + mSource = source; + } + + /** + * Sets the app-specific data that will be passed to the search activity if + * the user opens the source selector and chooses a source. + */ + public void setAppSearchData(Bundle appSearchData) { + mAppSearchData = appSearchData; + } + + /** + * Sets the initial query that will be passed to the search activity if + * the user opens the source selector and chooses a source. + */ + public void setQuery(String query) { + mQuery = query; + } + + public void setVisibility(int visibility) { + mView.setVisibility(visibility); + } + + /** + * Creates an intent for opening the search source selector activity. + * + * @param source The current search source. + * @param query The initial query that will be passed to the search activity if + * the user opens the source selector and chooses a source. + * @param appSearchData The app-specific data that will be passed to the search + * activity if the user opens the source selector and chooses a source. + */ + public static Intent createIntent(ComponentName source, String query, Bundle appSearchData) { + Intent intent = new Intent(SearchManager.INTENT_ACTION_SELECT_SEARCH_SOURCE); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_CLEAR_TOP + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); + Uri sourceUri = componentNameToUri(source); + if (sourceUri != null) { + intent.setDataAndType(sourceUri, APPLICATION_TYPE); + } + if (query != null) { + intent.putExtra(SearchManager.QUERY, query); + } + if (query != null) { + intent.putExtra(SearchManager.APP_DATA, appSearchData); + } + return intent; + } + + /** + * Gets the search source from which the given + * {@link SearchManager.INTENT_ACTION_SELECT_SEARCH_SOURCE} intent was sent. + */ + public static ComponentName getSource(Intent intent) { + return uriToComponentName(intent.getData()); + } + + private static Uri componentNameToUri(ComponentName name) { + if (name == null) return null; + // TODO: This URI format is specificed in android.provider.Applications which is @hidden + return new Uri.Builder() + .scheme(ContentResolver.SCHEME_CONTENT) + .authority("applications") + .appendEncodedPath("applications") + .appendPath(name.getPackageName()) + .appendPath(name.getClassName()) + .build(); + } + + private static ComponentName uriToComponentName(Uri uri) { + if (uri == null) return null; + List<String> path = uri.getPathSegments(); + if (path == null || path.size() != 3) return null; + String pkg = path.get(1); + String cls = path.get(2); + if (TextUtils.isEmpty(pkg) || TextUtils.isEmpty(cls)) return null; + return new ComponentName(pkg, cls); + } + + public void onClick(View v) { + trigger(); + } + + private void trigger() { + try { + Intent intent = createIntent(mSource, mQuery, mAppSearchData); + intent.setSourceBounds(getOnScreenRect(mIconView)); + mIconView.getContext().startActivity(intent); + } catch (ActivityNotFoundException ex) { + Log.e(TAG, "No source selector activity found", ex); + } + } + + // TODO: This code is replicated in lots of places: + // - android.provider.ContactsContract.QuickContact.showQuickContact() + // - android.widget.RemoteViews.setOnClickPendingIntent() + // - com.android.launcher2.Launcher.onClick() + // - com.android.launcher.Launcher.onClick() + // - com.android.server.status.StatusBarService.Launcher.onClick() + private static Rect getOnScreenRect(View v) { + final float appScale = v.getResources().getCompatibilityInfo().applicationScale; + final int[] pos = new int[2]; + v.getLocationOnScreen(pos); + final Rect rect = new Rect(); + rect.left = (int) (pos[0] * appScale + 0.5f); + rect.top = (int) (pos[1] * appScale + 0.5f); + rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f); + rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f); + return rect; + } + +} diff --git a/core/java/android/appwidget/AppWidgetHostView.java b/core/java/android/appwidget/AppWidgetHostView.java index 2f719f3..792b289 100644 --- a/core/java/android/appwidget/AppWidgetHostView.java +++ b/core/java/android/appwidget/AppWidgetHostView.java @@ -311,8 +311,8 @@ public class AppWidgetHostView extends FrameLayout { // Take requested dimensions from child, but apply default gravity. FrameLayout.LayoutParams requested = (FrameLayout.LayoutParams)view.getLayoutParams(); if (requested == null) { - requested = new FrameLayout.LayoutParams(LayoutParams.FILL_PARENT, - LayoutParams.FILL_PARENT); + requested = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, + LayoutParams.MATCH_PARENT); } requested.gravity = Gravity.CENTER; diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index fda9b81..7e5f858 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -83,6 +83,8 @@ public final class BluetoothA2dp { /** Default priority for a2dp devices that should not allow incoming * connections */ public static final int PRIORITY_OFF = 0; + /** Default priority when not set or when the device is unpaired */ + public static final int PRIORITY_UNDEFINED = -1; private final IBluetoothA2dp mService; private final Context mContext; diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 5eb655a..b792965 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -109,6 +109,8 @@ public final class BluetoothHeadset { /** Default priority for headsets that should not be auto-connected * and not allow incoming connections. */ public static final int PRIORITY_OFF = 0; + /** Default priority when not set or when the device is unpaired */ + public static final int PRIORITY_UNDEFINED = -1; /** The voice dialer 'works' but the user experience is poor. The voice * recognizer has trouble dealing with the 8kHz SCO signal, and it still diff --git a/core/java/android/bluetooth/package.html b/core/java/android/bluetooth/package.html index 4f0755e..5ff240c 100644 --- a/core/java/android/bluetooth/package.html +++ b/core/java/android/bluetooth/package.html @@ -12,96 +12,16 @@ devices, connecting with devices, and managing data transfer between devices. <li>Transfer data to and from other devices</li> </ul> -<p class="note"><strong>Note:</strong> +<p> To perform Bluetooth communication using these APIs, an application must declare the {@link android.Manifest.permission#BLUETOOTH} permission. Some -additional functionality, such as requesting device discovery and -pairing also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} +additional functionality, such as requesting device discovery, +also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission. </p> -<h3>Overview</h3> - -<p>Here's a basic introduction to the Bluetooth classes:</p> -<dl> - <dt>{@link android.bluetooth.BluetoothAdapter}</dt> - <dd>This represents the local Bluetooth adapter, which is essentially the - entry-point to performing any interaction with Bluetooth. With it, you can - discover other Bluetooth devices, query a list of bonded (paired) devices, - initialize a {@link android.bluetooth.BluetoothDevice} using a known MAC - address, and create a {@link android.bluetooth.BluetoothServerSocket} to - listen for communications from other devices.</dd> - - <dt>{@link android.bluetooth.BluetoothDevice}</dt> - <dd>This represents a remote Bluetooth device. Use this to request a - connection with a remote device through a - {@link android.bluetooth.BluetoothSocket} - or query information about the device such as its name, address, class, and - bonding state.</dd> - - <dt>{@link android.bluetooth.BluetoothSocket}</dt> - <dd>This represents the interface for a Bluetooth socket - (similar to a TCP client-side {@link java.net.Socket}). This is the - connection point that allows an app to transfer data with another Bluetooth - device via {@link java.io.InputStream} and {@link java.io.OutputStream}.</dd> - <dt>{@link android.bluetooth.BluetoothServerSocket}</dt> - - <dd>This represents an open server socket that listens for incoming requests - (similar to a TCP server-side {@link java.net.ServerSocket}). - When attempting to connect two Android devices, one device will need to open - a server socket with this class. When a connection is accepted, a new - {@link android.bluetooth.BluetoothSocket} will be returned, - which can be used to manage the connection and transfer data.</dd> - - <dt>{@link android.bluetooth.BluetoothClass}</dt> - <dd>This represents the Bluetooth class for a device which describes general - characteristics and capabilities of a device. This class and its subclasses - don't provide any actual functionality. The sub-classes are entirely composed - of constants for the device and service class definitions.</dd> -</dl> - - -<h3>Example Procedure</h3> - -<p>For example, here's an pseudo-code procedure for discovering and -connecting a remote device, and transfering data:</p> - -<ol> - <li>Register a {@link android.content.BroadcastReceiver} that accepts the - {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent.</li> - <li>Call {@link android.bluetooth.BluetoothAdapter#getDefaultAdapter} to - retrieve the Android system's local - {@link android.bluetooth.BluetoothAdapter}.</li> - <li>Call {@link android.bluetooth.BluetoothAdapter#startDiscovery() - BluetoothAdapter.startDiscovery()} to scan for local devices. This is where - the BroadcastReceiver comes in; Android now scans for devices and will - broadcast the {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent - for each remote device discovered. The - {@link android.content.BroadcastReceiver} - you created will receive each Intent.</li> - <li>The {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent - includes the {@link android.bluetooth.BluetoothDevice#EXTRA_DEVICE} - Parcelable extra, which is a {@link android.bluetooth.BluetoothDevice} - object. Extract this from the Intent and call - {@link android.bluetooth.BluetoothDevice#createRfcommSocketToServiceRecord(java.util.UUID) - BluetoothDevice.createRfcommSocketToServiceRecord()} - to open a {@link android.bluetooth.BluetoothSocket} with a chosen - remote device.</li> - <li>Call {@link android.bluetooth.BluetoothSocket#connect() - BluetoothSocket.connect()} to connect with the remote device.</li> - <li>When successfully connected, call - {@link android.bluetooth.BluetoothSocket#getInputStream() - BluetoothSocket.getInputStream()} and/or - {@link android.bluetooth.BluetoothSocket#getOutputStream() - BluetoothSocket.getOutputStream()} to retreive an - {@link java.io.InputStream} and {@link java.io.OutputStream}, respectively, - which are hooked into the socket.</li> - <li>Use {@link java.io.InputStream#read(byte[]) InputStream.read()} and - {@link java.io.OutputStream#write(byte[]) OutputStream.write()} to transfer - data.</li> -</ol> - - +<p>For a detailed guide to using the Bluetooth APIs, see the <a +href="{@docRoot}guide/topics/wireless/bluetooth.html">Bluetooth Dev Guide topic</a>.</p> <p class="note"><strong>Note:</strong> Not all Android devices are guaranteed to have Bluetooth functionality.</p> diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java index 6e7e6d7..eb2d7b1 100644 --- a/core/java/android/content/ContentResolver.java +++ b/core/java/android/content/ContentResolver.java @@ -88,11 +88,11 @@ public abstract class ContentResolver { * <code>content://com.company.provider.imap/inbox/1</code> for a particular * message in the inbox, whose MIME type would be reported as * <code>CURSOR_ITEM_BASE_TYPE + "/vnd.company.imap-msg"</code> - * + * * <p>Compare with {@link #CURSOR_DIR_BASE_TYPE}. */ public static final String CURSOR_ITEM_BASE_TYPE = "vnd.android.cursor.item"; - + /** * This is the Android platform's base MIME type for a content: URI * containing a Cursor of zero or more items. Applications should use this @@ -102,7 +102,7 @@ public abstract class ContentResolver { * <code>content://com.company.provider.imap/inbox</code> for all of the * messages in its inbox, whose MIME type would be reported as * <code>CURSOR_DIR_BASE_TYPE + "/vnd.company.imap-msg"</code> - * + * * <p>Note how the base MIME type varies between this and * {@link #CURSOR_ITEM_BASE_TYPE} depending on whether there is * one single item or multiple items in the data set, while the sub-type @@ -173,13 +173,25 @@ public abstract class ContentResolver { } /** + * <p> * Query the given URI, returning a {@link Cursor} over the result set. + * </p> + * <p> + * For best performance, the caller should follow these guidelines: + * <ul> + * <li>Provide an explicit projection, to prevent + * reading data from storage that aren't going to be used.</li> + * <li>Use question mark parameter markers such as 'phone=?' instead of + * explicit values in the {@code selection} parameter, so that queries + * that differ only by those values will be recognized as the same + * for caching purposes.</li> + * </ul> + * </p> * * @param uri The URI, using the content:// scheme, for the content to * retrieve. * @param projection A list of which columns to return. Passing null will - * return all columns, which is discouraged to prevent reading data - * from storage that isn't going to be used. + * return all columns, which is inefficient. * @param selection A filter declaring which rows to return, formatted as an * SQL WHERE clause (excluding the WHERE itself). Passing null will * return all rows for the given URI. @@ -225,10 +237,10 @@ public abstract class ContentResolver { * <li>android.resource ({@link #SCHEME_ANDROID_RESOURCE})</li> * <li>file ({@link #SCHEME_FILE})</li> * </ul> - * + * * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information * on these schemes. - * + * * @param uri The desired URI. * @return InputStream * @throws FileNotFoundException if the provided URI could not be opened. @@ -283,7 +295,7 @@ public abstract class ContentResolver { * * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information * on these schemes. - * + * * @param uri The desired URI. * @param mode May be "w", "wa", "rw", or "rwt". * @return OutputStream @@ -318,7 +330,7 @@ public abstract class ContentResolver { * * <p>See {@link #openAssetFileDescriptor(Uri, String)} for more information * on these schemes. - * + * * @param uri The desired URI to open. * @param mode The file mode to use, as per {@link ContentProvider#openFile * ContentProvider.openFile}. @@ -334,19 +346,19 @@ public abstract class ContentResolver { if (afd == null) { return null; } - + if (afd.getDeclaredLength() < 0) { // This is a full file! return afd.getParcelFileDescriptor(); } - + // Client can't handle a sub-section of a file, so close what // we got and bail with an exception. try { afd.close(); } catch (IOException e) { } - + throw new FileNotFoundException("Not a whole file"); } @@ -491,7 +503,7 @@ public abstract class ContentResolver { res.id = id; return res; } - + /** @hide */ static public int modeToMode(Uri uri, String mode) throws FileNotFoundException { int modeBits; @@ -518,7 +530,7 @@ public abstract class ContentResolver { } return modeBits; } - + /** * Inserts a row into a table at the given URL. * @@ -1146,7 +1158,7 @@ public abstract class ContentResolver { /** @hide */ public static final String CONTENT_SERVICE_NAME = "content"; - + /** @hide */ public static IContentService getContentService() { if (sContentService != null) { @@ -1158,7 +1170,7 @@ public abstract class ContentResolver { if (Config.LOGV) Log.v("ContentService", "default service = " + sContentService); return sContentService; } - + private static IContentService sContentService; private final Context mContext; private static final String TAG = "ContentResolver"; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 2ab5357..0fafe5d 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1314,7 +1314,7 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a - * {@blink android.appwidget.AppWidgetManager} for accessing AppWidgets. + * {@link android.appwidget.AppWidgetManager} for accessing AppWidgets. * * @hide * @see #getSystemService @@ -1323,7 +1323,7 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve an - * {@blink android.backup.IBackupManager IBackupManager} for communicating + * {@link android.backup.IBackupManager IBackupManager} for communicating * with the backup mechanism. * @hide * @@ -1333,7 +1333,7 @@ public abstract class Context { /** * Use with {@link #getSystemService} to retrieve a - * {@blink android.os.DropBox DropBox} instance for recording + * {@link android.os.DropBoxManager} instance for recording * diagnostic logs. * @see #getSystemService */ diff --git a/core/java/android/content/EventLogTags.logtags b/core/java/android/content/EventLogTags.logtags deleted file mode 100644 index af50a3c..0000000 --- a/core/java/android/content/EventLogTags.logtags +++ /dev/null @@ -1,10 +0,0 @@ -# See system/core/logcat/event.logtags for a description of the format of this file. - -option java_package android.content - -# --------------------------- -# SyncAdapter.java -# --------------------------- -# What happens in a sync operation (bytes sent and received, and -# operation details) -2743 sync_details (authority|3),(send|1|2),(recv|1|2),(details|3) diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index ec1252a..bf37b62 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -575,7 +575,7 @@ import java.util.Set; * {@link #setFlags} and {@link #addFlags}. See {@link #setFlags} for a list * of all possible flags. */ -public class Intent implements Parcelable { +public class Intent implements Parcelable, Cloneable { // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent activity actions (see action variable). diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index d436365..a9c61dc 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -859,8 +859,8 @@ class SyncManager implements OnAccountsUpdateListener { } // Cap the delay - long maxSyncRetryTimeInSeconds = Settings.Gservices.getLong(mContext.getContentResolver(), - Settings.Gservices.SYNC_MAX_RETRY_DELAY_IN_SECONDS, + long maxSyncRetryTimeInSeconds = Settings.Secure.getLong(mContext.getContentResolver(), + Settings.Secure.SYNC_MAX_RETRY_DELAY_IN_SECONDS, DEFAULT_MAX_SYNC_RETRY_TIME_IN_SECONDS); if (newDelayInMs > maxSyncRetryTimeInSeconds * 1000) { newDelayInMs = maxSyncRetryTimeInSeconds * 1000; diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index d1fdfaf..4c53201 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -25,7 +25,6 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.accounts.Account; -import android.backup.IBackupManager; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; @@ -37,7 +36,7 @@ import android.os.Message; import android.os.Parcel; import android.os.RemoteCallbackList; import android.os.RemoteException; -import android.os.ServiceManager; +import android.os.SystemProperties; import android.util.Log; import android.util.SparseArray; import android.util.Xml; @@ -258,7 +257,9 @@ public class SyncStorageEngine extends Handler { mCal = Calendar.getInstance(TimeZone.getTimeZone("GMT+0")); - File dataDir = Environment.getDataDirectory(); + // This call will return the correct directory whether Encrypted File Systems is + // enabled or not. + File dataDir = Environment.getSecureDataDirectory(); File systemDir = new File(dataDir, "system"); File syncDir = new File(systemDir, "sync"); mAccountInfoFile = new AtomicFile(new File(syncDir, "accounts.xml")); @@ -511,7 +512,7 @@ public class SyncStorageEngine extends Handler { SyncStatusInfo status = getOrCreateSyncStatusLocked(authority.ident); status.pending = true; - status.initialize = op.extras != null && + status.initialize = op.extras != null && op.extras.containsKey(ContentResolver.SYNC_EXTRAS_INITIALIZE) && op.extras.getBoolean(ContentResolver.SYNC_EXTRAS_INITIALIZE); } diff --git a/core/java/android/content/package.html b/core/java/android/content/package.html index dd5360f..eac679d 100644 --- a/core/java/android/content/package.html +++ b/core/java/android/content/package.html @@ -421,7 +421,7 @@ can supply them explicitly in the XML file:</p> <?xml version="1.0" encoding="utf-8"?> <root> <EditText id="text" - android:layout_width="fill_parent" android:layout_height="fill_parent" + android:layout_width="match_parent" android:layout_height="match_parent" <b>android:textSize="18" android:textColor="#008"</b> android:text="Hello, World!" /> </root> @@ -447,7 +447,7 @@ one of those resources:</p> <?xml version="1.0" encoding="utf-8"?> <root> <EditText id="text" - android:layout_width="fill_parent" android:layout_height="fill_parent" + android:layout_width="match_parent" android:layout_height="match_parent" <b>android:textColor="@color/opaque_red"</b> android:text="Hello, World!" /> </root> @@ -463,7 +463,7 @@ reference a system resource, you would need to write:</p> <?xml version="1.0" encoding="utf-8"?> <root> <EditText id="text" - android:layout_width="fill_parent" android:layout_height="fill_parent" + android:layout_width="match_parent" android:layout_height="match_parent" android:textColor="@<b>android:</b>color/opaque_red" android:text="Hello, World!" /> </root> @@ -476,7 +476,7 @@ strings in a layout file so that they can be localized:</p> <?xml version="1.0" encoding="utf-8"?> <root> <EditText id="text" - android:layout_width="fill_parent" android:layout_height="fill_parent" + android:layout_width="match_parent" android:layout_height="match_parent" android:textColor="@android:color/opaque_red" android:text="@string/hello_world" /> </root> @@ -509,7 +509,7 @@ one of the standard colors defined in the base system theme:</p> <?xml version="1.0" encoding="utf-8"?> <root> <EditText id="text" - android:layout_width="fill_parent" android:layout_height="fill_parent" + android:layout_width="match_parent" android:layout_height="match_parent" <b>android:textColor="?android:textDisabledColor"</b> android:text="@string/hello_world" /> </root> @@ -637,10 +637,10 @@ new style resource with the desired values:</p> <?xml version="1.0" encoding="utf-8"?> <root> <EditText id="text1" <b>style="@style/SpecialText"</b> - android:layout_width="fill_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Hello, World!" /> <EditText id="text2" <b>style="@style/SpecialText"</b> - android:layout_width="fill_parent" android:layout_height="wrap_content" + android:layout_width="match_parent" android:layout_height="wrap_content" android:text="I love you all." /> </root></pre> <h4> </h4> diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 1800c30..3dea286 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -208,6 +208,16 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_RESTORE_NEEDS_APPLICATION = 1<<16; /** + * Value for {@link #flags}: this is true if the application has set + * its android:neverEncrypt to true, false otherwise. It is used to specify + * that this package specifically "opts-out" of a secured file system solution, + * and will always store its data in-the-clear. + * + * {@hide} + */ + public static final int FLAG_NEVER_ENCRYPT = 1<<17; + + /** * Flags associated with the application. Any combination of * {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE}, * {@link #FLAG_PERSISTENT}, {@link #FLAG_FACTORY_TEST}, and diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 03d2a6d..53a966d 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -16,6 +16,8 @@ package android.content.pm; +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -518,30 +520,35 @@ public abstract class PackageManager { * {@link #hasSystemFeature}: The device has a camera facing away * from the screen. */ + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CAMERA = "android.hardware.camera"; /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device's camera supports auto-focus. */ + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CAMERA_AUTOFOCUS = "android.hardware.camera.autofocus"; /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device's camera supports flash. */ + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_CAMERA_FLASH = "android.hardware.camera.flash"; /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device includes a light sensor. */ + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_SENSOR_LIGHT = "android.hardware.sensor.light"; /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device includes a proximity sensor. */ + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_SENSOR_PROXIMITY = "android.hardware.sensor.proximity"; /** @@ -549,30 +556,35 @@ public abstract class PackageManager { * {@link #hasSystemFeature}: The device has a telephony radio with data * communication support. */ + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_TELEPHONY = "android.hardware.telephony"; /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has a CDMA telephony stack. */ + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma"; /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device has a GSM telephony stack. */ + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm"; /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device's touch screen supports multitouch. */ + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_TOUCHSCREEN_MULTITOUCH = "android.hardware.touchscreen.multitouch"; /** * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports live wallpapers. */ + @SdkConstant(SdkConstantType.FEATURE) public static final String FEATURE_LIVE_WALLPAPER = "android.software.live_wallpaper"; /** diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 0754cbf..ad99f54 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -1399,6 +1399,12 @@ public class PackageParser { ai.flags |= ApplicationInfo.FLAG_TEST_ONLY; } + if (sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_neverEncrypt, + false)) { + ai.flags |= ApplicationInfo.FLAG_NEVER_ENCRYPT; + } + String str; str = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestApplication_permission); diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java index 453a83d..70baaef 100644 --- a/core/java/android/content/res/ColorStateList.java +++ b/core/java/android/content/res/ColorStateList.java @@ -44,7 +44,7 @@ import java.util.Arrays; * <selector xmlns:android="http://schemas.android.com/apk/res/android"> * <item android:state_focused="true" android:color="@color/testcolor1"/> * <item android:state_pressed="true" android:state_enabled="false" android:color="@color/testcolor2" /> - * <item android:state_enabled="false" android:colore="@color/testcolor3" /> + * <item android:state_enabled="false" android:color="@color/testcolor3" /> * <item android:state_active="true" android:color="@color/testcolor4" /> * <item android:color="@color/testcolor5"/> * </selector> diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java index 70b9b83..b178d4f 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -582,21 +582,23 @@ public class SQLiteCursor extends AbstractWindowedCursor { @Override protected void finalize() { try { + // if the cursor hasn't been closed yet, close it first if (mWindow != null) { close(); - String message = "Finalizing cursor " + this + " on " + mEditTable - + " that has not been deactivated or closed"; + Log.e(TAG, "Finalizing cursor that has not been deactivated or closed." + + " database = " + mDatabase.getPath() + ", table = " + mEditTable + + ", query = " + mQuery.mSql); if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) { - Log.d(TAG, message + "\nThis cursor was created in:"); + Log.d(TAG, "This cursor was created in:"); for (StackTraceElement ste : mStackTraceElements) { Log.d(TAG, " " + ste); } } SQLiteDebug.notifyActiveCursorFinalized(); - throw new IllegalStateException(message); } else { if (Config.LOGV) { - Log.v(TAG, "Finalizing cursor " + this + " on " + mEditTable); + Log.v(TAG, "Finalizing cursor on database = " + mDatabase.getPath() + + ", table = " + mEditTable + ", query = " + mQuery.mSql); } } } finally { diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 657bbcc..f310586 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -323,6 +323,7 @@ public class SQLiteDatabase extends SQLiteClosable { close(); } finally { Log.e(TAG, "Removing corrupt database: " + mPath); + EventLog.writeEvent(EVENT_DB_CORRUPT, mPath); // Delete the corrupt file. Don't re-create it now -- that would just confuse people // -- but the next time someone tries to open it, they can set it up from scratch. new File(mPath).delete(); diff --git a/core/java/android/ddm/DdmHandleHello.java b/core/java/android/ddm/DdmHandleHello.java index c5d591f..0603ca5 100644 --- a/core/java/android/ddm/DdmHandleHello.java +++ b/core/java/android/ddm/DdmHandleHello.java @@ -152,8 +152,8 @@ public class DdmHandleHello extends ChunkHandler { "hprof-heap-dump", "method-trace-profiling" }; - if (Config.LOGD) - Log.d("ddm-heap", "Got feature list request"); + if (Config.LOGV) + Log.v("ddm-heap", "Got feature list request"); int size = 4 + 4 * features.length; for (int i = features.length-1; i >= 0; i--) diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index 271f973..0c6bb1a 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -364,7 +364,7 @@ public class SensorManager final float[] values = new float[3]; final int[] status = new int[1]; final long timestamp[] = new long[1]; - Process.setThreadPriority(Process.THREAD_PRIORITY_DISPLAY); + Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); if (!open()) { return; @@ -545,8 +545,8 @@ public class SensorManager i = sensors_module_get_next_sensor(sensor, i); if (i>=0) { - Log.d(TAG, "found sensor: " + sensor.getName() + - ", handle=" + sensor.getHandle()); + //Log.d(TAG, "found sensor: " + sensor.getName() + + // ", handle=" + sensor.getHandle()); sensor.setLegacyType(getLegacySensorType(sensor.getType())); fullList.add(sensor); sHandleToSensor.append(sensor.getHandle(), sensor); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 6978974..5d5bd9c 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -16,7 +16,7 @@ package android.inputmethodservice; -import static android.view.ViewGroup.LayoutParams.FILL_PARENT; +import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; import android.app.Dialog; @@ -556,7 +556,7 @@ public class InputMethodService extends AbstractInputMethodService { Context.LAYOUT_INFLATER_SERVICE); mWindow = new SoftInputWindow(this, mTheme, mDispatcherState); initViews(); - mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT); + mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT); } /** @@ -803,8 +803,8 @@ public class InputMethodService extends AbstractInputMethodService { * candidates only mode changes. * * <p>The default implementation makes the layout for the window - * FILL_PARENT x FILL_PARENT when in fullscreen mode, and - * FILL_PARENT x WRAP_CONTENT when in non-fullscreen mode. + * MATCH_PARENT x MATCH_PARENT when in fullscreen mode, and + * MATCH_PARENT x WRAP_CONTENT when in non-fullscreen mode. * * @param win The input method's window. * @param isFullscreen If true, the window is running in fullscreen mode @@ -816,9 +816,9 @@ public class InputMethodService extends AbstractInputMethodService { public void onConfigureWindow(Window win, boolean isFullscreen, boolean isCandidatesOnly) { if (isFullscreen) { - mWindow.getWindow().setLayout(FILL_PARENT, FILL_PARENT); + mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT); } else { - mWindow.getWindow().setLayout(FILL_PARENT, WRAP_CONTENT); + mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT); } } @@ -1056,8 +1056,8 @@ public class InputMethodService extends AbstractInputMethodService { public void setExtractView(View view) { mExtractFrame.removeAllViews(); mExtractFrame.addView(view, new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.FILL_PARENT)); + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); mExtractView = view; if (view != null) { mExtractEditText = (ExtractEditText)view.findViewById( @@ -1086,7 +1086,7 @@ public class InputMethodService extends AbstractInputMethodService { public void setCandidatesView(View view) { mCandidatesFrame.removeAllViews(); mCandidatesFrame.addView(view, new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); } @@ -1099,7 +1099,7 @@ public class InputMethodService extends AbstractInputMethodService { public void setInputView(View view) { mInputFrame.removeAllViews(); mInputFrame.addView(view, new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); mInputView = view; } diff --git a/core/java/android/inputmethodservice/KeyboardView.java b/core/java/android/inputmethodservice/KeyboardView.java index 76131fc..b0c3909 100755 --- a/core/java/android/inputmethodservice/KeyboardView.java +++ b/core/java/android/inputmethodservice/KeyboardView.java @@ -1118,6 +1118,11 @@ public class KeyboardView extends View implements View.OnClickListener { if (action == MotionEvent.ACTION_DOWN) mSwipeTracker.clear(); mSwipeTracker.addMovement(me); + // Ignore all motion events until a DOWN. + if (mAbortKey && action != MotionEvent.ACTION_DOWN) { + return true; + } + if (mGestureDetector.onTouchEvent(me)) { showPreview(NOT_A_KEY); mHandler.removeMessages(MSG_REPEAT); @@ -1150,9 +1155,14 @@ public class KeyboardView extends View implements View.OnClickListener { mKeys[keyIndex].codes[0] : 0); if (mCurrentKey >= 0 && mKeys[mCurrentKey].repeatable) { mRepeatKeyIndex = mCurrentKey; - repeatKey(); Message msg = mHandler.obtainMessage(MSG_REPEAT); mHandler.sendMessageDelayed(msg, REPEAT_START_DELAY); + repeatKey(); + // Delivering the key could have caused an abort + if (mAbortKey) { + mRepeatKeyIndex = NOT_A_KEY; + break; + } } if (mCurrentKey != NOT_A_KEY) { Message msg = mHandler.obtainMessage(MSG_LONGPRESS, me); diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index 538e51a..b254961 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -60,12 +60,11 @@ public class MobileDataStateTracker extends NetworkStateTracker { * @param apnType the Phone apnType * @param tag the name of this network */ - public MobileDataStateTracker(Context context, Handler target, - int netType, String apnType, String tag) { + public MobileDataStateTracker(Context context, Handler target, int netType, String tag) { super(context, target, netType, TelephonyManager.getDefault().getNetworkType(), tag, TelephonyManager.getDefault().getNetworkTypeName()); - mApnType = apnType; + mApnType = networkTypeToApnType(netType); mPhoneService = null; if(netType == ConnectivityManager.TYPE_MOBILE) { mEnabled = true; @@ -501,4 +500,22 @@ public class MobileDataStateTracker extends NetworkStateTracker { + " APN type \"" + apnType + "\""); return Phone.APN_REQUEST_FAILED; } + + public static String networkTypeToApnType(int netType) { + switch(netType) { + case ConnectivityManager.TYPE_MOBILE: + return Phone.APN_TYPE_DEFAULT; // TODO - use just one of these + case ConnectivityManager.TYPE_MOBILE_MMS: + return Phone.APN_TYPE_MMS; + case ConnectivityManager.TYPE_MOBILE_SUPL: + return Phone.APN_TYPE_SUPL; + case ConnectivityManager.TYPE_MOBILE_DUN: + return Phone.APN_TYPE_DUN; + case ConnectivityManager.TYPE_MOBILE_HIPRI: + return Phone.APN_TYPE_HIPRI; + default: + Log.e(TAG, "Error mapping networkType " + netType + " to apnType."); + return null; + } + } } diff --git a/core/java/android/net/SSLCertificateSocketFactory.java b/core/java/android/net/SSLCertificateSocketFactory.java index a97b9e5..e40f1b8 100644 --- a/core/java/android/net/SSLCertificateSocketFactory.java +++ b/core/java/android/net/SSLCertificateSocketFactory.java @@ -16,11 +16,12 @@ package android.net; -import android.net.http.DomainNameChecker; import android.os.SystemProperties; import android.util.Config; import android.util.Log; +import com.android.common.DomainNameValidator; + import java.io.IOException; import java.net.InetAddress; import java.net.Socket; @@ -200,7 +201,7 @@ public class SSLCertificateSocketFactory extends SSLSocketFactory { X509Certificate lastChainCert = (X509Certificate) certs[0]; - if (!DomainNameChecker.match(lastChainCert, destHost)) { + if (!DomainNameValidator.match(lastChainCert, destHost)) { if (Config.LOGD) { Log.d(LOG_TAG,"validateSocket(): domain name check failed"); } diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java index 9a1b65d..f2ea539 100644 --- a/core/java/android/net/Uri.java +++ b/core/java/android/net/Uri.java @@ -1567,51 +1567,40 @@ public abstract class Uri implements Parcelable, Comparable<Uri> { if (isOpaque()) { throw new UnsupportedOperationException(NOT_HIERARCHICAL); } + if (key == null) { + throw new NullPointerException("key"); + } - String query = getEncodedQuery(); - + final String query = getEncodedQuery(); if (query == null) { return null; } - String encodedKey; - try { - encodedKey = URLEncoder.encode(key, DEFAULT_ENCODING); - } catch (UnsupportedEncodingException e) { - throw new AssertionError(e); - } - - String prefix = encodedKey + "="; + final String encodedKey = encode(key, null); + final int encodedKeyLength = encodedKey.length(); - if (query.length() < prefix.length()) { - return null; - } + int encodedKeySearchIndex = 0; + final int encodedKeySearchEnd = query.length() - (encodedKeyLength + 1); - int start; - if (query.startsWith(prefix)) { - // It's the first parameter. - start = prefix.length(); - } else { - // It must be later in the query string. - prefix = "&" + prefix; - start = query.indexOf(prefix); - - if (start == -1) { - // Not found. - return null; + while (encodedKeySearchIndex <= encodedKeySearchEnd) { + int keyIndex = query.indexOf(encodedKey, encodedKeySearchIndex); + if (keyIndex == -1) { + break; + } + final int equalsIndex = keyIndex + encodedKeyLength; + if (query.charAt(equalsIndex) != '=') { + encodedKeySearchIndex = equalsIndex + 1; + continue; + } + if (keyIndex == 0 || query.charAt(keyIndex - 1) == '&') { + int end = query.indexOf('&', equalsIndex); + if (end == -1) { + end = query.length(); + } + return decode(query.substring(equalsIndex + 1, end)); } - - start += prefix.length(); - } - - // Find end of value. - int end = query.indexOf('&', start); - if (end == -1) { - end = query.length(); } - - String value = query.substring(start, end); - return decode(value); + return null; } /** Identifies a null parcelled Uri. */ diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java index ed6b4c2..da6af9d 100644 --- a/core/java/android/net/http/CertificateChainValidator.java +++ b/core/java/android/net/http/CertificateChainValidator.java @@ -16,6 +16,8 @@ package android.net.http; +import com.android.common.DomainNameValidator; + import org.apache.harmony.xnet.provider.jsse.SSLParameters; import java.io.IOException; @@ -112,7 +114,7 @@ class CertificateChainValidator { closeSocketThrowException( sslSocket, "certificate for this site is null"); } else { - if (!DomainNameChecker.match(currCertificate, domain)) { + if (!DomainNameValidator.match(currCertificate, domain)) { String errorMessage = "certificate not for this host: " + domain; if (HttpLog.LOGV) { diff --git a/core/java/android/net/http/DomainNameChecker.java b/core/java/android/net/http/DomainNameChecker.java deleted file mode 100644 index 3e01d2c..0000000 --- a/core/java/android/net/http/DomainNameChecker.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * Copyright (C) 2008 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 android.net.http; - -import org.bouncycastle.asn1.x509.X509Name; - -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.security.cert.X509Certificate; -import java.security.cert.CertificateParsingException; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import java.util.regex.PatternSyntaxException; -import java.util.Vector; - -/** - * Implements basic domain-name validation as specified by RFC2818. - * - * {@hide} - */ -public class DomainNameChecker { - private static Pattern QUICK_IP_PATTERN; - static { - try { - QUICK_IP_PATTERN = Pattern.compile("^[a-f0-9\\.:]+$"); - } catch (PatternSyntaxException e) {} - } - - private static final int ALT_DNS_NAME = 2; - private static final int ALT_IPA_NAME = 7; - - /** - * Checks the site certificate against the domain name of the site being visited - * @param certificate The certificate to check - * @param thisDomain The domain name of the site being visited - * @return True iff if there is a domain match as specified by RFC2818 - */ - public static boolean match(X509Certificate certificate, String thisDomain) { - if (certificate == null || thisDomain == null || thisDomain.length() == 0) { - return false; - } - - thisDomain = thisDomain.toLowerCase(); - if (!isIpAddress(thisDomain)) { - return matchDns(certificate, thisDomain); - } else { - return matchIpAddress(certificate, thisDomain); - } - } - - /** - * @return True iff the domain name is specified as an IP address - */ - private static boolean isIpAddress(String domain) { - boolean rval = (domain != null && domain.length() != 0); - if (rval) { - try { - // do a quick-dirty IP match first to avoid DNS lookup - rval = QUICK_IP_PATTERN.matcher(domain).matches(); - if (rval) { - rval = domain.equals( - InetAddress.getByName(domain).getHostAddress()); - } - } catch (UnknownHostException e) { - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = "unknown host exception"; - } - - if (HttpLog.LOGV) { - HttpLog.v("DomainNameChecker.isIpAddress(): " + errorMessage); - } - - rval = false; - } - } - - return rval; - } - - /** - * Checks the site certificate against the IP domain name of the site being visited - * @param certificate The certificate to check - * @param thisDomain The DNS domain name of the site being visited - * @return True iff if there is a domain match as specified by RFC2818 - */ - private static boolean matchIpAddress(X509Certificate certificate, String thisDomain) { - if (HttpLog.LOGV) { - HttpLog.v("DomainNameChecker.matchIpAddress(): this domain: " + thisDomain); - } - - try { - Collection subjectAltNames = certificate.getSubjectAlternativeNames(); - if (subjectAltNames != null) { - Iterator i = subjectAltNames.iterator(); - while (i.hasNext()) { - List altNameEntry = (List)(i.next()); - if (altNameEntry != null && 2 <= altNameEntry.size()) { - Integer altNameType = (Integer)(altNameEntry.get(0)); - if (altNameType != null) { - if (altNameType.intValue() == ALT_IPA_NAME) { - String altName = (String)(altNameEntry.get(1)); - if (altName != null) { - if (HttpLog.LOGV) { - HttpLog.v("alternative IP: " + altName); - } - if (thisDomain.equalsIgnoreCase(altName)) { - return true; - } - } - } - } - } - } - } - } catch (CertificateParsingException e) {} - - return false; - } - - /** - * Checks the site certificate against the DNS domain name of the site being visited - * @param certificate The certificate to check - * @param thisDomain The DNS domain name of the site being visited - * @return True iff if there is a domain match as specified by RFC2818 - */ - private static boolean matchDns(X509Certificate certificate, String thisDomain) { - boolean hasDns = false; - try { - Collection subjectAltNames = certificate.getSubjectAlternativeNames(); - if (subjectAltNames != null) { - Iterator i = subjectAltNames.iterator(); - while (i.hasNext()) { - List altNameEntry = (List)(i.next()); - if (altNameEntry != null && 2 <= altNameEntry.size()) { - Integer altNameType = (Integer)(altNameEntry.get(0)); - if (altNameType != null) { - if (altNameType.intValue() == ALT_DNS_NAME) { - hasDns = true; - String altName = (String)(altNameEntry.get(1)); - if (altName != null) { - if (matchDns(thisDomain, altName)) { - return true; - } - } - } - } - } - } - } - } catch (CertificateParsingException e) { - // one way we can get here is if an alternative name starts with - // '*' character, which is contrary to one interpretation of the - // spec (a valid DNS name must start with a letter); there is no - // good way around this, and in order to be compatible we proceed - // to check the common name (ie, ignore alternative names) - if (HttpLog.LOGV) { - String errorMessage = e.getMessage(); - if (errorMessage == null) { - errorMessage = "failed to parse certificate"; - } - - if (HttpLog.LOGV) { - HttpLog.v("DomainNameChecker.matchDns(): " + errorMessage); - } - } - } - - if (!hasDns) { - X509Name xName = new X509Name(certificate.getSubjectDN().getName()); - Vector val = xName.getValues(); - Vector oid = xName.getOIDs(); - for (int i = 0; i < oid.size(); i++) { - if (oid.elementAt(i).equals(X509Name.CN)) { - return matchDns(thisDomain, (String)(val.elementAt(i))); - } - } - } - - return false; - } - - /** - * @param thisDomain The domain name of the site being visited - * @param thatDomain The domain name from the certificate - * @return True iff thisDomain matches thatDomain as specified by RFC2818 - */ - private static boolean matchDns(String thisDomain, String thatDomain) { - if (HttpLog.LOGV) { - HttpLog.v("DomainNameChecker.matchDns():" + - " this domain: " + thisDomain + - " that domain: " + thatDomain); - } - - if (thisDomain == null || thisDomain.length() == 0 || - thatDomain == null || thatDomain.length() == 0) { - return false; - } - - thatDomain = thatDomain.toLowerCase(); - - // (a) domain name strings are equal, ignoring case: X matches X - boolean rval = thisDomain.equals(thatDomain); - if (!rval) { - String[] thisDomainTokens = thisDomain.split("\\."); - String[] thatDomainTokens = thatDomain.split("\\."); - - int thisDomainTokensNum = thisDomainTokens.length; - int thatDomainTokensNum = thatDomainTokens.length; - - // (b) OR thatHost is a '.'-suffix of thisHost: Z.Y.X matches X - if (thisDomainTokensNum >= thatDomainTokensNum) { - for (int i = thatDomainTokensNum - 1; i >= 0; --i) { - rval = thisDomainTokens[i].equals(thatDomainTokens[i]); - if (!rval) { - // (c) OR we have a special *-match: - // *.Y.X matches Z.Y.X but *.X doesn't match Z.Y.X - rval = (i == 0 && thisDomainTokensNum == thatDomainTokensNum); - if (rval) { - rval = thatDomainTokens[0].equals("*"); - if (!rval) { - // (d) OR we have a *-component match: - // f*.com matches foo.com but not bar.com - rval = domainTokenMatch( - thisDomainTokens[0], thatDomainTokens[0]); - } - } - break; - } - } - } else { - // (e) OR thatHost has a '*.'-prefix of thisHost: - // *.Y.X matches Y.X - rval = thatDomain.equals("*." + thisDomain); - } - } - - return rval; - } - - /** - * @param thisDomainToken The domain token from the current domain name - * @param thatDomainToken The domain token from the certificate - * @return True iff thisDomainToken matches thatDomainToken, using the - * wildcard match as specified by RFC2818-3.1. For example, f*.com must - * match foo.com but not bar.com - */ - private static boolean domainTokenMatch(String thisDomainToken, String thatDomainToken) { - if (thisDomainToken != null && thatDomainToken != null) { - int starIndex = thatDomainToken.indexOf('*'); - if (starIndex >= 0) { - if (thatDomainToken.length() - 1 <= thisDomainToken.length()) { - String prefix = thatDomainToken.substring(0, starIndex); - String suffix = thatDomainToken.substring(starIndex + 1); - - return thisDomainToken.startsWith(prefix) && thisDomainToken.endsWith(suffix); - } - } - } - - return false; - } -} diff --git a/core/java/android/os/AsyncTask.java b/core/java/android/os/AsyncTask.java index 7d2c698..d28148c 100644 --- a/core/java/android/os/AsyncTask.java +++ b/core/java/android/os/AsyncTask.java @@ -82,7 +82,7 @@ import java.util.concurrent.atomic.AtomicInteger; * <li><code>Result</code>, the type of the result of the background * computation.</li> * </ol> - * <p>Not all types are always used by am asynchronous task. To mark a type as unused, + * <p>Not all types are always used by an asynchronous task. To mark a type as unused, * simply use the type {@link Void}:</p> * <pre> * private class MyTask extends AsyncTask<Void, Void, Void> { ... } diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index e9353d8..fcd8f38 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -21,7 +21,7 @@ package android.os; */ public class Build { /** Value used for when a build property is unknown. */ - private static final String UNKNOWN = "unknown"; + public static final String UNKNOWN = "unknown"; /** Either a changelist number, or a label like "M4-rc20". */ public static final String ID = getString("ro.build.id"); @@ -41,6 +41,9 @@ public class Build { /** The name of the instruction set (CPU type + ABI convention) of native code. */ public static final String CPU_ABI = getString("ro.product.cpu.abi"); + /** The name of the second instruction set (CPU type + ABI convention) of native code. */ + public static final String CPU_ABI2 = getString("ro.product.cpu.abi2"); + /** The manufacturer of the product/hardware. */ public static final String MANUFACTURER = getString("ro.product.manufacturer"); @@ -50,6 +53,15 @@ public class Build { /** The end-user-visible name for the end product. */ public static final String MODEL = getString("ro.product.model"); + /** @pending The system bootloader version number. */ + public static final String BOOTLOADER = getString("ro.bootloader"); + + /** @pending The radio firmware version number. */ + public static final String RADIO = getString("gsm.version.baseband"); + + /** @pending The device serial number. */ + public static final String SERIAL = getString("ro.serialno"); + /** Various version strings. */ public static class VERSION { /** diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java index b4f64b6..b33e8be 100644 --- a/core/java/android/os/Debug.java +++ b/core/java/android/os/Debug.java @@ -753,6 +753,16 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo } /** + * Dumps the contents of VM reference tables (e.g. JNI locals and + * globals) to the log file. + * + * @hide + */ + public static final void dumpReferenceTables() { + VMDebug.dumpReferenceTables(); + } + + /** * API for gathering and querying instruction counts. * * Example usage: diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index f761e8e..9491bd4 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -18,6 +18,8 @@ package android.os; import java.io.File; +import android.os.IMountService; + /** * Provides access to environment variables. */ @@ -26,6 +28,10 @@ public class Environment { private static final File ROOT_DIRECTORY = getDirectory("ANDROID_ROOT", "/system"); + private static final String SYSTEM_PROPERTY_EFS_ENABLED = "persist.security.efs.enabled"; + + private static IMountService mMntSvc = null; + /** * Gets the Android root directory. */ @@ -33,9 +39,55 @@ public class Environment { return ROOT_DIRECTORY; } + /** + * Gets the system directory available for secure storage. + * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure/system). + * Otherwise, it returns the unencrypted /data/system directory. + * @return File object representing the secure storage system directory. + * @hide + */ + public static File getSystemSecureDirectory() { + if (isEncryptedFilesystemEnabled()) { + return new File(SECURE_DATA_DIRECTORY, "system"); + } else { + return new File(DATA_DIRECTORY, "system"); + } + } + + /** + * Gets the data directory for secure storage. + * If Encrypted File system is enabled, it returns an encrypted directory (/data/secure). + * Otherwise, it returns the unencrypted /data directory. + * @return File object representing the data directory for secure storage. + * @hide + */ + public static File getSecureDataDirectory() { + if (isEncryptedFilesystemEnabled()) { + return SECURE_DATA_DIRECTORY; + } else { + return DATA_DIRECTORY; + } + } + + /** + * Returns whether the Encrypted File System feature is enabled on the device or not. + * @return <code>true</code> if Encrypted File System feature is enabled, <code>false</code> + * if disabled. + * @hide + */ + public static boolean isEncryptedFilesystemEnabled() { + return SystemProperties.getBoolean(SYSTEM_PROPERTY_EFS_ENABLED, false); + } + private static final File DATA_DIRECTORY = getDirectory("ANDROID_DATA", "/data"); + /** + * @hide + */ + private static final File SECURE_DATA_DIRECTORY + = getDirectory("ANDROID_SECURE_DATA", "/data/secure"); + private static final File EXTERNAL_STORAGE_DIRECTORY = getDirectory("EXTERNAL_STORAGE", "/sdcard"); @@ -119,9 +171,19 @@ public class Environment { /** * Gets the current state of the external storage device. + * Note: This call should be deprecated as it doesn't support + * multiple volumes. */ public static String getExternalStorageState() { - return SystemProperties.get("EXTERNAL_STORAGE_STATE", MEDIA_REMOVED); + try { + if (mMntSvc == null) { + mMntSvc = IMountService.Stub.asInterface(ServiceManager + .getService("mount")); + } + return mMntSvc.getVolumeState(getExternalStorageDirectory().toString()); + } catch (Exception rex) { + return Environment.MEDIA_REMOVED; + } } static File getDirectory(String variableName, String defaultPath) { diff --git a/core/java/android/os/FileObserver.java b/core/java/android/os/FileObserver.java index 38d252e..3457815 100644 --- a/core/java/android/os/FileObserver.java +++ b/core/java/android/os/FileObserver.java @@ -103,9 +103,7 @@ public abstract class FileObserver { try { observer.onEvent(mask, path); } catch (Throwable throwable) { - Log.e(LOG_TAG, "Unhandled throwable " + throwable.toString() + - " (returned by observer " + observer + ")", throwable); - RuntimeInit.crash("FileObserver", throwable); + Log.wtf(LOG_TAG, "Unhandled exception in FileObserver " + observer, throwable); } } } diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java index 51dfb5b..4780cf3 100644 --- a/core/java/android/os/FileUtils.java +++ b/core/java/android/os/FileUtils.java @@ -153,14 +153,16 @@ public class FileUtils public static String readTextFile(File file, int max, String ellipsis) throws IOException { InputStream input = new FileInputStream(file); try { - if (max > 0) { // "head" mode: read the first N bytes + long size = file.length(); + if (max > 0 || (size > 0 && max == 0)) { // "head" mode: read the first N bytes + if (size > 0 && (max == 0 || size < max)) max = (int) size; byte[] data = new byte[max + 1]; int length = input.read(data); if (length <= 0) return ""; if (length <= max) return new String(data, 0, length); if (ellipsis == null) return new String(data, 0, max); return new String(data, 0, max) + ellipsis; - } else if (max < 0) { // "tail" mode: read it all, keep the last N + } else if (max < 0) { // "tail" mode: keep the last N int len; boolean rolled = false; byte[] last = null, data = null; @@ -180,7 +182,7 @@ public class FileUtils } if (ellipsis == null || !rolled) return new String(last); return ellipsis + new String(last); - } else { // "cat" mode: read it all + } else { // "cat" mode: size unknown, read it all in streaming fashion ByteArrayOutputStream contents = new ByteArrayOutputStream(); int len; byte[] data = new byte[1024]; diff --git a/core/java/android/os/ICheckinService.aidl b/core/java/android/os/ICheckinService.aidl index 619079a..e5609b0 100644 --- a/core/java/android/os/ICheckinService.aidl +++ b/core/java/android/os/ICheckinService.aidl @@ -29,6 +29,9 @@ interface ICheckinService { /** Reboot into the recovery system and wipe all user data. */ void masterClear(); + /** Reboot into the recovery system, wipe all user data and enable Encrypted File Systems. */ + void masterClearAndToggleEFS(boolean efsEnabled); + /** * Determine if the device is under parental control. Return null if * we are unable to check the parental control status. diff --git a/core/java/android/os/IMountService.aidl b/core/java/android/os/IMountService.aidl index 96d44b6..e73569a 100644 --- a/core/java/android/os/IMountService.aidl +++ b/core/java/android/os/IMountService.aidl @@ -42,17 +42,17 @@ interface IMountService /** * Mount external storage at given mount point. */ - void mountMedia(String mountPoint); + void mountVolume(String mountPoint); /** * Safely unmount external storage at given mount point. */ - void unmountMedia(String mountPoint); + void unmountVolume(String mountPoint); /** * Format external storage given a mount point. */ - void formatMedia(String mountPoint); + void formatVolume(String mountPoint); /** * Returns true if media notification sounds are enabled. @@ -65,14 +65,46 @@ interface IMountService void setPlayNotificationSounds(boolean value); /** - * Returns true if USB Mass Storage is automatically started - * when a UMS host is detected. + * Gets the state of an volume via it's mountpoint. */ - boolean getAutoStartUms(); + String getVolumeState(String mountPoint); + + /* + * Creates a secure container with the specified parameters. + * On success, the filesystem container-path is returned. + */ + String createSecureContainer(String id, int sizeMb, String fstype, String key, int ownerUid); + + /* + * Finalize a container which has just been created and populated. + * After finalization, the container is immutable. + */ + void finalizeSecureContainer(String id); + + /* + * Destroy a secure container, and free up all resources associated with it. + * NOTE: Ensure all references are released prior to deleting. + */ + void destroySecureContainer(String id); + + /* + * Mount a secure container with the specified key and owner UID. + * On success, the filesystem container-path is returned. + */ + String mountSecureContainer(String id, String key, int ownerUid); + + /* + * Returns the filesystem path of a mounted secure container. + */ + String getSecureContainerPath(String id); + + /** + * Gets an Array of currently known secure container IDs + */ + String[] getSecureContainerList(); /** - * Sets whether or not USB Mass Storage is automatically started - * when a UMS host is detected. + * Shuts down the MountService and gracefully unmounts all external media. */ - void setAutoStartUms(boolean value); + void shutdown(); } diff --git a/core/java/android/os/IPowerManager.aidl b/core/java/android/os/IPowerManager.aidl index 0afc537..23762ca 100644 --- a/core/java/android/os/IPowerManager.aidl +++ b/core/java/android/os/IPowerManager.aidl @@ -31,6 +31,7 @@ interface IPowerManager void preventScreenOn(boolean prevent); boolean isScreenOn(); void reboot(String reason); + void crash(String message); // sets the brightness of the backlights (screen, keyboard, button) 0-255 void setBacklightBrightness(int brightness); diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java index 03542dd..9742b05 100644 --- a/core/java/android/os/MemoryFile.java +++ b/core/java/android/os/MemoryFile.java @@ -52,7 +52,7 @@ public class MemoryFile private static native void native_write(FileDescriptor fd, int address, byte[] buffer, int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException; - private static native int native_get_mapped_size(FileDescriptor fd) throws IOException; + private static native int native_get_size(FileDescriptor fd) throws IOException; private FileDescriptor mFD; // ashmem file descriptor private int mAddress; // address of ashmem memory @@ -300,20 +300,19 @@ public class MemoryFile * @hide */ public static boolean isMemoryFile(FileDescriptor fd) throws IOException { - return (native_get_mapped_size(fd) >= 0); + return (native_get_size(fd) >= 0); } /** - * Returns the size of the memory file, rounded up to a page boundary, that - * the file descriptor refers to, or -1 if the file descriptor does not - * refer to a memory file. + * Returns the size of the memory file that the file descriptor refers to, + * or -1 if the file descriptor does not refer to a memory file. * * @throws IOException If <code>fd</code> is not a valid file descriptor. * * @hide */ - public static int getMappedSize(FileDescriptor fd) throws IOException { - return native_get_mapped_size(fd); + public static int getSize(FileDescriptor fd) throws IOException { + return native_get_size(fd); } /** diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index caf0923..bc653d6 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -115,9 +115,7 @@ public class MessageQueue { didIdle = true; keep = ((IdleHandler)idler).queueIdle(); } catch (Throwable t) { - Log.e("MessageQueue", - "IdleHandler threw exception", t); - RuntimeInit.crash("MessageQueue", t); + Log.wtf("MessageQueue", "IdleHandler threw exception", t); } if (!keep) { diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java index 3679e47..bc76180 100644 --- a/core/java/android/os/Power.java +++ b/core/java/android/os/Power.java @@ -17,6 +17,8 @@ package android.os; import java.io.IOException; +import android.os.ServiceManager; +import android.os.IMountService; /** * Class that provides access to some of the power management functions. @@ -97,5 +99,19 @@ public class Power * @throws IOException if reboot fails for some reason (eg, lack of * permission) */ - public static native void reboot(String reason) throws IOException; + public static void reboot(String reason) throws IOException + { + IMountService mSvc = IMountService.Stub.asInterface( + ServiceManager.getService("mount")); + + if (mSvc != null) { + try { + mSvc.shutdown(); + } catch (Exception e) { + } + } + rebootNative(reason); + } + + private static native void rebootNative(String reason) throws IOException ; } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index 293dabc..e4eaf45 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -326,12 +326,11 @@ public class PowerManager { synchronized (mToken) { if (mHeld) { + Log.wtf(TAG, "WakeLock finalized while still held: " + mTag); try { mService.releaseWakeLock(mToken, 0); } catch (RemoteException e) { } - RuntimeInit.crash(TAG, new Exception( - "WakeLock finalized while still held: "+mTag)); } } } diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java new file mode 100644 index 0000000..3dd3918 --- /dev/null +++ b/core/java/android/os/RecoverySystem.java @@ -0,0 +1,418 @@ +/* + * 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 android.os; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.security.GeneralSecurityException; +import java.security.PublicKey; +import java.security.Signature; +import java.security.SignatureException; +import java.security.cert.Certificate; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.util.Collection; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import android.content.Context; +import android.util.Log; + +import org.apache.harmony.security.asn1.BerInputStream; +import org.apache.harmony.security.pkcs7.ContentInfo; +import org.apache.harmony.security.pkcs7.SignedData; +import org.apache.harmony.security.pkcs7.SignerInfo; +import org.apache.harmony.security.provider.cert.X509CertImpl; + +/** + * RecoverySystem contains methods for interacting with the Android + * recovery system (the separate partition that can be used to install + * system updates, wipe user data, etc.) + */ +public class RecoverySystem { + private static final String TAG = "RecoverySystem"; + + /** + * Default location of zip file containing public keys (X509 + * certs) authorized to sign OTA updates. + */ + private static final File DEFAULT_KEYSTORE = + new File("/system/etc/security/otacerts.zip"); + + /** Send progress to listeners no more often than this (in ms). */ + private static final long PUBLISH_PROGRESS_INTERVAL_MS = 500; + + /** Used to communicate with recovery. See bootable/recovery/recovery.c. */ + private static File RECOVERY_DIR = new File("/cache/recovery"); + private static File COMMAND_FILE = new File(RECOVERY_DIR, "command"); + private static File LOG_FILE = new File(RECOVERY_DIR, "log"); + + // Length limits for reading files. + private static int LOG_FILE_MAX_LENGTH = 8 * 1024; + + /** + * Interface definition for a callback to be invoked regularly as + * verification proceeds. + */ + public interface ProgressListener { + /** + * Called periodically as the verification progresses. + * + * @param progress the approximate percentage of the + * verification that has been completed, ranging from 0 + * to 100 (inclusive). + */ + public void onProgress(int progress); + } + + /** @return the set of certs that can be used to sign an OTA package. */ + private static HashSet<Certificate> getTrustedCerts(File keystore) + throws IOException, GeneralSecurityException { + HashSet<Certificate> trusted = new HashSet<Certificate>(); + if (keystore == null) { + keystore = DEFAULT_KEYSTORE; + } + ZipFile zip = new ZipFile(keystore); + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + Enumeration<? extends ZipEntry> entries = zip.entries(); + while (entries.hasMoreElements()) { + ZipEntry entry = entries.nextElement(); + trusted.add(cf.generateCertificate(zip.getInputStream(entry))); + } + } finally { + zip.close(); + } + return trusted; + } + + /** + * Verify the cryptographic signature of a system update package + * before installing it. Note that the package is also verified + * separately by the installer once the device is rebooted into + * the recovery system. This function will return only if the + * package was successfully verified; otherwise it will throw an + * exception. + * + * Verification of a package can take significant time, so this + * function should not be called from a UI thread. + * + * @param packageFile the package to be verified + * @param listener an object to receive periodic progress + * updates as verification proceeds. May be null. + * @param deviceCertsZipFile the zip file of certificates whose + * public keys we will accept. Verification succeeds if the + * package is signed by the private key corresponding to any + * public key in this file. May be null to use the system default + * file (currently "/system/etc/security/otacerts.zip"). + * + * @throws IOException if there were any errors reading the + * package or certs files. + * @throws GeneralSecurityException if verification failed + */ + public static void verifyPackage(File packageFile, + ProgressListener listener, + File deviceCertsZipFile) + throws IOException, GeneralSecurityException { + long fileLen = packageFile.length(); + + RandomAccessFile raf = new RandomAccessFile(packageFile, "r"); + try { + int lastPercent = 0; + long lastPublishTime = System.currentTimeMillis(); + if (listener != null) { + listener.onProgress(lastPercent); + } + + raf.seek(fileLen - 6); + byte[] footer = new byte[6]; + raf.readFully(footer); + + if (footer[2] != (byte)0xff || footer[3] != (byte)0xff) { + throw new SignatureException("no signature in file (no footer)"); + } + + int commentSize = (footer[4] & 0xff) | ((footer[5] & 0xff) << 8); + int signatureStart = (footer[0] & 0xff) | ((footer[1] & 0xff) << 8); + Log.v(TAG, String.format("comment size %d; signature start %d", + commentSize, signatureStart)); + + byte[] eocd = new byte[commentSize + 22]; + raf.seek(fileLen - (commentSize + 22)); + raf.readFully(eocd); + + // Check that we have found the start of the + // end-of-central-directory record. + if (eocd[0] != (byte)0x50 || eocd[1] != (byte)0x4b || + eocd[2] != (byte)0x05 || eocd[3] != (byte)0x06) { + throw new SignatureException("no signature in file (bad footer)"); + } + + for (int i = 4; i < eocd.length-3; ++i) { + if (eocd[i ] == (byte)0x50 && eocd[i+1] == (byte)0x4b && + eocd[i+2] == (byte)0x05 && eocd[i+3] == (byte)0x06) { + throw new SignatureException("EOCD marker found after start of EOCD"); + } + } + + // The following code is largely copied from + // JarUtils.verifySignature(). We could just *call* that + // method here if that function didn't read the entire + // input (ie, the whole OTA package) into memory just to + // compute its message digest. + + BerInputStream bis = new BerInputStream( + new ByteArrayInputStream(eocd, commentSize+22-signatureStart, signatureStart)); + ContentInfo info = (ContentInfo)ContentInfo.ASN1.decode(bis); + SignedData signedData = info.getSignedData(); + if (signedData == null) { + throw new IOException("signedData is null"); + } + Collection encCerts = signedData.getCertificates(); + if (encCerts.isEmpty()) { + throw new IOException("encCerts is empty"); + } + // Take the first certificate from the signature (packages + // should contain only one). + Iterator it = encCerts.iterator(); + X509Certificate cert = null; + if (it.hasNext()) { + cert = new X509CertImpl((org.apache.harmony.security.x509.Certificate)it.next()); + } else { + throw new SignatureException("signature contains no certificates"); + } + + List sigInfos = signedData.getSignerInfos(); + SignerInfo sigInfo; + if (!sigInfos.isEmpty()) { + sigInfo = (SignerInfo)sigInfos.get(0); + } else { + throw new IOException("no signer infos!"); + } + + // Check that the public key of the certificate contained + // in the package equals one of our trusted public keys. + + HashSet<Certificate> trusted = getTrustedCerts( + deviceCertsZipFile == null ? DEFAULT_KEYSTORE : deviceCertsZipFile); + + PublicKey signatureKey = cert.getPublicKey(); + boolean verified = false; + for (Certificate c : trusted) { + if (c.getPublicKey().equals(signatureKey)) { + verified = true; + break; + } + } + if (!verified) { + throw new SignatureException("signature doesn't match any trusted key"); + } + + // The signature cert matches a trusted key. Now verify that + // the digest in the cert matches the actual file data. + + // The verifier in recovery *only* handles SHA1withRSA + // signatures. SignApk.java always uses SHA1withRSA, no + // matter what the cert says to use. Ignore + // cert.getSigAlgName(), and instead use whatever + // algorithm is used by the signature (which should be + // SHA1withRSA). + + String da = sigInfo.getdigestAlgorithm(); + String dea = sigInfo.getDigestEncryptionAlgorithm(); + String alg = null; + if (da == null || dea == null) { + // fall back to the cert algorithm if the sig one + // doesn't look right. + alg = cert.getSigAlgName(); + } else { + alg = da + "with" + dea; + } + Signature sig = Signature.getInstance(alg); + sig.initVerify(cert); + + // The signature covers all of the OTA package except the + // archive comment and its 2-byte length. + long toRead = fileLen - commentSize - 2; + long soFar = 0; + raf.seek(0); + byte[] buffer = new byte[4096]; + while (soFar < toRead) { + int size = buffer.length; + if (soFar + size > toRead) { + size = (int)(toRead - soFar); + } + int read = raf.read(buffer, 0, size); + sig.update(buffer, 0, read); + soFar += read; + + if (listener != null) { + long now = System.currentTimeMillis(); + int p = (int)(soFar * 100 / toRead); + if (p > lastPercent && + now - lastPublishTime > PUBLISH_PROGRESS_INTERVAL_MS) { + lastPercent = p; + lastPublishTime = now; + listener.onProgress(lastPercent); + } + } + } + if (listener != null) { + listener.onProgress(100); + } + + if (!sig.verify(sigInfo.getEncryptedDigest())) { + throw new SignatureException("signature digest verification failed"); + } + } finally { + raf.close(); + } + } + + /** + * Reboots the device in order to install the given update + * package. + * Requires the {@link android.Manifest.permission#REBOOT} + * and {@link android.Manifest.permission#ACCESS_CACHE_FILESYSTEM} + * permissions. + * + * @param context the Context to use + * @param packageFile the update package to install. Currently + * must be on the /cache or /data partitions. + * + * @throws IOException if writing the recovery command file + * fails, or if the reboot itself fails. + */ + public static void installPackage(Context context, File packageFile) + throws IOException { + String filename = packageFile.getCanonicalPath(); + + if (filename.startsWith("/cache/")) { + filename = "CACHE:" + filename.substring(7); + } else if (filename.startsWith("/data/")) { + filename = "DATA:" + filename.substring(6); + } else { + throw new IllegalArgumentException( + "Must start with /cache or /data: " + filename); + } + Log.w(TAG, "!!! REBOOTING TO INSTALL " + filename + " !!!"); + String arg = "--update_package=" + filename; + bootCommand(context, arg); + } + + /** + * Reboots the device and wipes the user data partition. This is + * sometimes called a "factory reset", which is something of a + * misnomer because the system partition is not restored to its + * factory state. + * Requires the {@link android.Manifest.permission#REBOOT} + * and {@link android.Manifest.permission#ACCESS_CACHE_FILESYSTEM} + * permissions. + * + * @param context the Context to use + * + * @throws IOException if writing the recovery command file + * fails, or if the reboot itself fails. + */ + public static void rebootWipeUserData(Context context) + throws IOException { + bootCommand(context, "--wipe_data"); + } + + /** + * Reboot into the recovery system to wipe the /data partition and toggle + * Encrypted File Systems on/off. + * @param extras to add to the RECOVERY_COMPLETED intent after rebooting. + * @throws IOException if something goes wrong. + * + * @hide + */ + public static void rebootToggleEFS(Context context, boolean efsEnabled) + throws IOException { + if (efsEnabled) { + bootCommand(context, "--set_encrypted_filesystem=on"); + } else { + bootCommand(context, "--set_encrypted_filesystem=off"); + } + } + + /** + * Reboot into the recovery system with the supplied argument. + * @param arg to pass to the recovery utility. + * @throws IOException if something goes wrong. + */ + private static void bootCommand(Context context, String arg) throws IOException { + RECOVERY_DIR.mkdirs(); // In case we need it + COMMAND_FILE.delete(); // In case it's not writable + LOG_FILE.delete(); + + FileWriter command = new FileWriter(COMMAND_FILE); + try { + command.write(arg); + command.write("\n"); + } finally { + command.close(); + } + + // Having written the command file, go ahead and reboot + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + pm.reboot("recovery"); + + throw new IOException("Reboot failed (no permissions?)"); + } + + /** + * Called after booting to process and remove recovery-related files. + * @return the log file from recovery, or null if none was found. + * + * @hide + */ + public static String handleAftermath() { + // Record the tail of the LOG_FILE + String log = null; + try { + log = FileUtils.readTextFile(LOG_FILE, -LOG_FILE_MAX_LENGTH, "...\n"); + } catch (FileNotFoundException e) { + Log.i(TAG, "No recovery log file"); + } catch (IOException e) { + Log.e(TAG, "Error reading recovery log", e); + } + + // Delete everything in RECOVERY_DIR + String[] names = RECOVERY_DIR.list(); + for (int i = 0; names != null && i < names.length; i++) { + File f = new File(RECOVERY_DIR, names[i]); + if (!f.delete()) { + Log.e(TAG, "Can't delete: " + f); + } else { + Log.i(TAG, "Deleted: " + f); + } + } + + return log; + } + + private void RecoverySystem() { } // Do not instantiate +} diff --git a/core/java/android/pim/vcard/VCardComposer.java b/core/java/android/pim/vcard/VCardComposer.java index 4eaea6a..389c9f4 100644 --- a/core/java/android/pim/vcard/VCardComposer.java +++ b/core/java/android/pim/vcard/VCardComposer.java @@ -54,6 +54,7 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.lang.reflect.Method; import java.nio.charset.UnsupportedCharsetException; import java.util.ArrayList; import java.util.Arrays; @@ -197,7 +198,7 @@ public class VCardComposer { if (mIsDoCoMo) { try { // Create one empty entry. - mWriter.write(createOneEntryInternal("-1")); + mWriter.write(createOneEntryInternal("-1", null)); } catch (IOException e) { Log.e(LOG_TAG, "IOException occurred during exportOneContactData: " @@ -428,6 +429,14 @@ public class VCardComposer { } public boolean createOneEntry() { + return createOneEntry(null); + } + + /** + * @param getEntityIteratorMethod For Dependency Injection. + * @hide just for testing. + */ + public boolean createOneEntry(Method getEntityIteratorMethod) { if (mCursor == null || mCursor.isAfterLast()) { mErrorReason = FAILURE_REASON_NOT_INITIALIZED; return false; @@ -439,7 +448,8 @@ public class VCardComposer { vcard = createOneCallLogEntryInternal(); } else { if (mIdColumn >= 0) { - vcard = createOneEntryInternal(mCursor.getString(mIdColumn)); + vcard = createOneEntryInternal(mCursor.getString(mIdColumn), + getEntityIteratorMethod); } else { Log.e(LOG_TAG, "Incorrect mIdColumn: " + mIdColumn); return true; @@ -475,7 +485,8 @@ public class VCardComposer { return true; } - private String createOneEntryInternal(final String contactId) { + private String createOneEntryInternal(final String contactId, + Method getEntityIteratorMethod) { final Map<String, List<ContentValues>> contentValuesListMap = new HashMap<String, List<ContentValues>>(); // The resolver may return the entity iterator with no data. It is possiible. @@ -484,13 +495,34 @@ public class VCardComposer { boolean dataExists = false; EntityIterator entityIterator = null; try { - final Uri uri = RawContacts.CONTENT_URI.buildUpon() - .appendEncodedPath(contactId) - .appendEncodedPath(RawContacts.Entity.CONTENT_DIRECTORY) - .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1") - .build(); - entityIterator = RawContacts.newEntityIterator(mContentResolver.query( - uri, null, null, null, null)); + + if (getEntityIteratorMethod != null) { + try { + final Uri uri = RawContacts.CONTENT_URI.buildUpon() + .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1") + .build(); + final String selection = Data.CONTACT_ID + "=?"; + final String[] selectionArgs = new String[] {contactId}; + entityIterator = (EntityIterator)getEntityIteratorMethod.invoke(null, + mContentResolver, uri, selection, selectionArgs, null); + } catch (Exception e) { + e.printStackTrace(); + } + } else { + final Uri uri = RawContacts.CONTENT_URI.buildUpon() + .appendEncodedPath(contactId) + .appendEncodedPath(RawContacts.Entity.CONTENT_DIRECTORY) + .appendQueryParameter(Data.FOR_EXPORT_ONLY, "1") + .build(); + entityIterator = RawContacts.newEntityIterator(mContentResolver.query( + uri, null, null, null, null)); + } + + if (entityIterator == null) { + Log.e(LOG_TAG, "EntityIterator is null"); + return ""; + } + dataExists = entityIterator.hasNext(); while (entityIterator.hasNext()) { Entity entity = entityIterator.next(); diff --git a/core/java/android/pim/vcard/VCardParser_V21.java b/core/java/android/pim/vcard/VCardParser_V21.java index e7c19cf..c2928cb 100644 --- a/core/java/android/pim/vcard/VCardParser_V21.java +++ b/core/java/android/pim/vcard/VCardParser_V21.java @@ -110,11 +110,11 @@ public class VCardParser_V21 extends VCardParser { private long mTimeHandleBase64; public VCardParser_V21() { - this(VCardConfig.PARSE_TYPE_UNKNOWN); + this(null); } public VCardParser_V21(VCardSourceDetector detector) { - this(detector.getEstimatedType()); + this(detector != null ? detector.getEstimatedType() : VCardConfig.PARSE_TYPE_UNKNOWN); } public VCardParser_V21(int parseType) { diff --git a/core/java/android/preference/EditTextPreference.java b/core/java/android/preference/EditTextPreference.java index 84ee950..aa27627 100644 --- a/core/java/android/preference/EditTextPreference.java +++ b/core/java/android/preference/EditTextPreference.java @@ -28,7 +28,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.ViewParent; import android.widget.EditText; -import android.widget.LinearLayout; /** * A {@link Preference} that allows for string @@ -128,7 +127,7 @@ public class EditTextPreference extends DialogPreference { ViewGroup container = (ViewGroup) dialogView .findViewById(com.android.internal.R.id.edittext_container); if (container != null) { - container.addView(editText, ViewGroup.LayoutParams.FILL_PARENT, + container.addView(editText, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } } diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/Calendar.java index b74564a..509aac5 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/Calendar.java @@ -583,14 +583,14 @@ public final class Calendar { * {@link #ACCOUNT_TYPE} identifies a specific account. * <P>Type: TEXT</P> */ - public static final String ACCOUNT_NAME = "account_name"; + public static final String ACCOUNT_NAME = "_sync_account"; /** * The type of account to which this row belongs, which when paired with * {@link #ACCOUNT_NAME} identifies a specific account. * <P>Type: TEXT</P> */ - public static final String ACCOUNT_TYPE = "account_type"; + public static final String ACCOUNT_TYPE = "_sync_account_type"; public static EntityIterator newEntityIterator(Cursor cursor, ContentResolver resolver) { return new EntityIteratorImpl(cursor, resolver); @@ -1049,58 +1049,39 @@ public final class Calendar { public static final String MAX_INSTANCE = "maxInstance"; /** - * The minimum Julian day in the BusyBits table. + * The minimum Julian day in the EventDays table. * <P>Type: INTEGER</P> */ - public static final String MIN_BUSYBITS = "minBusyBits"; + public static final String MIN_EVENTDAYS = "minEventDays"; /** - * The maximum Julian day in the BusyBits table. + * The maximum Julian day in the EventDays table. * <P>Type: INTEGER</P> */ - public static final String MAX_BUSYBITS = "maxBusyBits"; + public static final String MAX_EVENTDAYS = "maxEventDays"; } public static final class CalendarMetaData implements CalendarMetaDataColumns { } - public interface BusyBitsColumns { + public interface EventDaysColumns { /** - * The Julian day number. + * The Julian starting day number. * <P>Type: INTEGER (int)</P> */ - public static final String DAY = "day"; + public static final String STARTDAY = "startDay"; + public static final String ENDDAY = "endDay"; - /** - * The 24 bits representing the 24 1-hour time slots in a day. - * If an event in the Instances table overlaps part of a 1-hour - * time slot then the corresponding bit is set. The first time slot - * (12am to 1am) is bit 0. The last time slot (11pm to midnight) - * is bit 23. - * <P>Type: INTEGER (int)</P> - */ - public static final String BUSYBITS = "busyBits"; - - /** - * The number of all-day events that occur on this day. - * <P>Type: INTEGER (int)</P> - */ - public static final String ALL_DAY_COUNT = "allDayCount"; } - public static final class BusyBits implements BusyBitsColumns { - public static final Uri CONTENT_URI = Uri.parse("content://calendar/busybits/when"); - - public static final String[] PROJECTION = { DAY, BUSYBITS, ALL_DAY_COUNT }; - - // The number of minutes represented by one busy bit - public static final int MINUTES_PER_BUSY_INTERVAL = 60; + public static final class EventDays implements EventDaysColumns { + public static final Uri CONTENT_URI = Uri.parse("content://calendar/instances/groupbyday"); - // The number of intervals in a day - public static final int INTERVALS_PER_DAY = 24 * 60 / MINUTES_PER_BUSY_INTERVAL; + public static final String[] PROJECTION = { STARTDAY, ENDDAY }; + public static final String SELECTION = "selected==1"; /** - * Retrieves the busy bits for the Julian days starting at "startDay" + * Retrieves the days with events for the Julian days starting at "startDay" * for "numDays". * * @param cr the ContentResolver @@ -1116,8 +1097,8 @@ public final class Calendar { Uri.Builder builder = CONTENT_URI.buildUpon(); ContentUris.appendId(builder, startDay); ContentUris.appendId(builder, endDay); - return cr.query(builder.build(), PROJECTION, null /* selection */, - null /* selection args */, DAY); + return cr.query(builder.build(), PROJECTION, SELECTION, + null /* selection args */, STARTDAY); } } diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index ed9198e..7fb9daf 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -23,10 +23,10 @@ import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; -import android.content.Intent; import android.content.CursorEntityIterator; -import android.content.EntityIterator; import android.content.Entity; +import android.content.EntityIterator; +import android.content.Intent; import android.content.res.Resources; import android.database.Cursor; import android.database.DatabaseUtils; @@ -35,6 +35,7 @@ import android.graphics.Rect; import android.net.Uri; import android.os.RemoteException; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Pair; import android.view.View; @@ -52,49 +53,50 @@ import java.io.InputStream; * ContactsContract defines an extensible database of contact-related * information. Contact information is stored in a three-tier data model: * </p> - * <blockquote> - * <p> + * <ul> + * <li> * The {@link Data} table contains all kinds of personal data: phone numbers, * email addresses etc. The list of data kinds that can be stored in this table * is open-ended. There is a predefined set of common kinds, but any application * can add its own data kinds. - * </p> - * <p> + * </li> + * <li> * A row in the {@link RawContacts} table represents a set of Data describing a - * person and associated with a single account. - * </p> - * <p> + * person and associated with a single account (for example, a single Gmail + * account). + * </li> + * <li> * A row in the {@link Contacts} table represents an aggregate of one or more * RawContacts presumably describing the same person. - * </p> - * </blockquote> + * </li> + * </ul> * <p> * Other tables include: * </p> - * <blockquote> - * <p> + * <ul> + * <li> * {@link Groups}, which contains information about raw contact groups - the * current API does not support the notion of groups spanning multiple accounts. - * </p> - * <p> + * </li> + * <li> * {@link StatusUpdates}, which contains social status updates including IM * availability. - * </p> - * <p> + * </li> + * <li> * {@link AggregationExceptions}, which is used for manual aggregation and * disaggregation of raw contacts - * </p> - * <p> + * </li> + * <li> * {@link Settings}, which contains visibility and sync settings for accounts * and groups. - * </p> - * <p> + * </li> + * <li> * {@link SyncState}, which contains free-form data maintained on behalf of sync * adapters - * </p> - * <p> - * {@link PhoneLookup}, which is used for quick caller-ID lookup</id> - * </blockquote> + * </li> + * <li> + * {@link PhoneLookup}, which is used for quick caller-ID lookup</li> + * </ul> */ @SuppressWarnings("unused") public final class ContactsContract { @@ -244,6 +246,9 @@ public final class ContactsContract { } /** + * Columns of {@link ContactsContract.Contacts} that track the user's + * preferences for, or interactions with, the contact. + * * @see Contacts * @see RawContacts * @see ContactsContract.Data @@ -270,20 +275,25 @@ public final class ContactsContract { public static final String STARRED = "starred"; /** - * A custom ringtone associated with a contact. Not always present. + * URI for a custom ringtone associated with the contact. If null or missing, + * the default ringtone is used. * <P>Type: TEXT (URI to the ringtone)</P> */ public static final String CUSTOM_RINGTONE = "custom_ringtone"; /** - * Whether the contact should always be sent to voicemail. Not always - * present. + * Whether the contact should always be sent to voicemail. If missing, + * defaults to false. * <P>Type: INTEGER (0 for false, 1 for true)</P> */ public static final String SEND_TO_VOICEMAIL = "send_to_voicemail"; } /** + * Columns of {@link ContactsContract.Contacts} that refer to intrinsic + * properties of the contact, as opposed to the user-specified options + * found in {@link ContactOptionsColumns}. + * * @see Contacts * @see ContactsContract.Data * @see PhoneLookup @@ -294,7 +304,7 @@ public final class ContactsContract { * The display name for the contact. * <P>Type: TEXT</P> */ - public static final String DISPLAY_NAME = "display_name"; + public static final String DISPLAY_NAME = ContactNameColumns.DISPLAY_NAME_PRIMARY; /** * Reference to the row in the RawContacts table holding the contact name. @@ -440,6 +450,8 @@ public final class ContactsContract { } /** + * Contact name and contact name metadata columns in the RawContacts table. + * * @see Contacts * @see RawContacts * @hide @@ -456,13 +468,13 @@ public final class ContactsContract { * The default text shown as the contact's display name. It is based on * available data, see {@link #DISPLAY_NAME_SOURCE}. */ - public static final String DISPLAY_NAME = "display_name"; + public static final String DISPLAY_NAME_PRIMARY = "display_name"; /** * Alternative representation of the display name. If display name is * based on the structured name and the structured name follows * the Western full name style, then this field contains the "family name first" - * version of the full name. Otherwise, it is the same as {@link #DISPLAY_NAME}. + * version of the full name. Otherwise, it is the same as DISPLAY_NAME_PRIMARY. */ public static final String DISPLAY_NAME_ALTERNATIVE = "display_name_alt"; @@ -483,7 +495,7 @@ public final class ContactsContract { * the sort key is the name's Pinyin spelling; for Japanese names * it is the Hiragana version of the phonetic name. */ - public static final String SORT_KEY = "sort_key"; + public static final String SORT_KEY_PRIMARY = "sort_key"; /** * Sort key based on the alternative representation of the full name, @@ -562,7 +574,7 @@ public final class ContactsContract { * </tr> * <tr> * <td>String</td> - * <td>{@link #DISPLAY_NAME}</td> + * <td>DISPLAY_NAME_PRIMARY</td> * <td>read-only</td> * <td>The display name for the contact. It is the display name * contributed by the raw contact referred to by the NAME_RAW_CONTACT_ID @@ -692,7 +704,7 @@ public final class ContactsContract { * </table> */ public static class Contacts implements BaseColumns, ContactsColumns, - ContactOptionsColumns, ContactStatusColumns { + ContactOptionsColumns, ContactNameColumns, ContactStatusColumns { /** * This utility class cannot be instantiated */ @@ -1519,6 +1531,7 @@ public final class ContactsContract { super(cursor); } + @Override public android.content.Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException { final int columnRawContactId = cursor.getColumnIndexOrThrow(RawContacts._ID); @@ -1614,14 +1627,34 @@ public final class ContactsContract { @Deprecated public static final String PRESENCE_STATUS = PRESENCE; - /* - * Presence Status definition + /** + * An allowed value of {@link #PRESENCE}. */ int OFFLINE = 0; + + /** + * An allowed value of {@link #PRESENCE}. + */ int INVISIBLE = 1; + + /** + * An allowed value of {@link #PRESENCE}. + */ int AWAY = 2; + + /** + * An allowed value of {@link #PRESENCE}. + */ int IDLE = 3; + + /** + * An allowed value of {@link #PRESENCE}. + */ int DO_NOT_DISTURB = 4; + + /** + * An allowed value of {@link #PRESENCE}. + */ int AVAILABLE = 5; /** @@ -1633,6 +1666,7 @@ public final class ContactsContract { /** * @deprecated use {@link #STATUS} */ + @Deprecated public static final String PRESENCE_CUSTOM_STATUS = STATUS; /** @@ -1689,7 +1723,7 @@ public final class ContactsContract { public static final String RAW_CONTACT_ID = "raw_contact_id"; /** - * Whether this is the primary entry of its kind for the raw contact it belongs to + * Whether this is the primary entry of its kind for the raw contact it belongs to. * <P>Type: INTEGER (if set, non-0 means true)</P> */ public static final String IS_PRIMARY = "is_primary"; @@ -1738,7 +1772,10 @@ public final class ContactsContract { public static final String DATA13 = "data13"; /** Generic data column, the meaning is {@link #MIMETYPE} specific */ public static final String DATA14 = "data14"; - /** Generic data column, the meaning is {@link #MIMETYPE} specific */ + /** + * Generic data column, the meaning is {@link #MIMETYPE} specific. By convention, + * this field is used to store BLOBs (binary data). + */ public static final String DATA15 = "data15"; /** Generic column for use by sync adapters. */ @@ -1757,30 +1794,35 @@ public final class ContactsContract { * @see ContactsContract.Data */ protected interface DataColumnsWithJoins extends BaseColumns, DataColumns, StatusColumns, - RawContactsColumns, ContactsColumns, ContactOptionsColumns, ContactStatusColumns { - + RawContactsColumns, ContactsColumns, ContactNameColumns, ContactOptionsColumns, + ContactStatusColumns { } /** * <p> * Constants for the data table, which contains data points tied to a raw - * contact. For example, a phone number or email address. + * contact. Each row of the data table is typically used to store a single + * piece of contact + * information (such as a phone number) and its + * associated metadata (such as whether it is a work or home number). * </p> * <h3>Data kinds</h3> * <p> - * Data is a generic table that can hold all kinds of data. Sync adapters - * and applications can introduce their own data kinds. The kind of data - * stored in a particular row is determined by the mime type in the row. - * Fields from {@link #DATA1} through {@link #DATA15} are generic columns - * whose specific use is determined by the kind of data stored in the row. + * Data is a generic table that can hold any kind of contact data. + * The kind of data stored in a given row is specified by the row's + * {@link #MIMETYPE} value, which determines the meaning of the + * generic columns {@link #DATA1} through + * {@link #DATA15}. * For example, if the data kind is - * {@link CommonDataKinds.Phone Phone.CONTENT_ITEM_TYPE}, then DATA1 stores the + * {@link CommonDataKinds.Phone Phone.CONTENT_ITEM_TYPE}, then the column + * {@link #DATA1} stores the * phone number, but if the data kind is - * {@link CommonDataKinds.Email Email.CONTENT_ITEM_TYPE}, then DATA1 stores the - * email address. + * {@link CommonDataKinds.Email Email.CONTENT_ITEM_TYPE}, then {@link #DATA1} + * stores the email address. + * Sync adapters and applications can introduce their own data kinds. * </p> * <p> - * ContactsContract defines a small number of common data kinds, e.g. + * ContactsContract defines a small number of pre-defined data kinds, e.g. * {@link CommonDataKinds.Phone}, {@link CommonDataKinds.Email} etc. As a * convenience, these classes define data kind specific aliases for DATA1 etc. * For example, {@link CommonDataKinds.Phone Phone.NUMBER} is the same as @@ -1797,8 +1839,8 @@ public final class ContactsContract { * By convention, {@link #DATA15} is used for storing BLOBs (binary data). * </p> * <p> - * Typically you should refrain from introducing new kinds of data for 3rd - * party account types. For example, if you add a data row for + * Typically you should refrain from introducing new kinds of data for an other + * party's account types. For example, if you add a data row for * "favorite song" to a raw contact owned by a Google account, it will not * get synced to the server, because the Google sync adapter does not know * how to handle this data kind. Thus new data kinds are typically @@ -1935,6 +1977,10 @@ public final class ContactsContract { * </dd> * </dl> * <h2>Columns</h2> + * <p> + * Many columns are available via a {@link Data#CONTENT_URI} query. For best performance you + * should explicitly specify a projection to only those columns that you need. + * </p> * <table class="jd-sumtable"> * <tr> * <th colspan='4'>Data</th> @@ -1944,7 +1990,7 @@ public final class ContactsContract { * <td style="width: 20em;">{@link #_ID}</td> * <td style="width: 5em;">read-only</td> * <td>Row ID. Sync adapter should try to preserve row IDs during updates. In other words, - * it would be a bad idea to delete and reinsert a data rows. A sync adapter should + * it would be a bad idea to delete and reinsert a data row. A sync adapter should * always do an update instead.</td> * </tr> * <tr> @@ -1976,21 +2022,15 @@ public final class ContactsContract { * <td>long</td> * <td>{@link #RAW_CONTACT_ID}</td> * <td>read/write-once</td> - * <td>A reference to the {@link RawContacts#_ID} that this data belongs to.</td> - * </tr> - * <tr> - * <td>long</td> - * <td>{@link #CONTACT_ID}</td> - * <td>read-only</td> - * <td>A reference to the {@link ContactsContract.Contacts#_ID} that this data row belongs - * to. It is obtained through a join with RawContacts.</td> + * <td>The id of the row in the {@link RawContacts} table that this data belongs to.</td> * </tr> * <tr> * <td>int</td> * <td>{@link #IS_PRIMARY}</td> * <td>read/write</td> * <td>Whether this is the primary entry of its kind for the raw contact it belongs to. - * "1" if true, "0" if false.</td> + * "1" if true, "0" if false. + * </td> * </tr> * <tr> * <td>int</td> @@ -1998,7 +2038,9 @@ public final class ContactsContract { * <td>read/write</td> * <td>Whether this is the primary entry of its kind for the aggregate * contact it belongs to. Any data record that is "super primary" must - * also be "primary".</td> + * also be "primary". For example, the super-primary entry may be + * interpreted as the default contact value of its kind (for example, + * the default phone number to use for the contact).</td> * </tr> * <tr> * <td>int</td> @@ -2027,7 +2069,19 @@ public final class ContactsContract { * {@link #DATA15} * </td> * <td>read/write</td> - * <td>Generic data columns, the meaning is {@link #MIMETYPE} specific.</td> + * <td> + * <p> + * Generic data columns. The meaning of each column is determined by the + * {@link #MIMETYPE}. By convention, {@link #DATA15} is used for storing + * BLOBs (binary data). + * </p> + * <p> + * Data columns whose meaning is not explicitly defined for a given MIMETYPE + * should not be used. There is no guarantee that any sync adapter will + * preserve them. Sync adapters themselves should not use such columns either, + * but should instead use {@link #SYNC1}-{@link #SYNC4}. + * </p> + * </td> * </tr> * <tr> * <td>Any type</td> @@ -2044,6 +2098,10 @@ public final class ContactsContract { * </tr> * </table> * + * <p> + * Some columns from the most recent associated status update are also available + * through an implicit join. + * </p> * <table class="jd-sumtable"> * <tr> * <th colspan='4'>Join with {@link StatusUpdates}</th> @@ -2096,18 +2154,26 @@ public final class ContactsContract { * </table> * * <p> - * Columns from the associated raw contact are also available through an - * implicit join. + * Some columns from the associated raw contact are also available through an + * implicit join. The other columns are excluded as uninteresting in this + * context. * </p> * * <table class="jd-sumtable"> * <tr> - * <th colspan='4'>Join with {@link RawContacts}</th> + * <th colspan='4'>Join with {@link ContactsContract.RawContacts}</th> * </tr> * <tr> - * <td style="width: 7em;">int</td> - * <td style="width: 20em;">{@link #AGGREGATION_MODE}</td> + * <td style="width: 7em;">long</td> + * <td style="width: 20em;">{@link #CONTACT_ID}</td> * <td style="width: 5em;">read-only</td> + * <td>The id of the row in the {@link Contacts} table that this data belongs + * to.</td> + * </tr> + * <tr> + * <td>int</td> + * <td>{@link #AGGREGATION_MODE}</td> + * <td>read-only</td> * <td>See {@link RawContacts}.</td> * </tr> * <tr> @@ -2119,13 +2185,18 @@ public final class ContactsContract { * </table> * * <p> - * Columns from the associated aggregated contact are also available through an - * implicit join. + * The ID column for the associated aggregated contact table + * {@link ContactsContract.Contacts} is available + * via the implicit join to the {@link RawContacts} table, see above. + * The remaining columns from this table are also + * available, through an implicit join. This + * facilitates lookup by + * the value of a single data element, such as the email address. * </p> * * <table class="jd-sumtable"> * <tr> - * <th colspan='4'>Join with {@link Contacts}</th> + * <th colspan='4'>Join with {@link ContactsContract.Contacts}</th> * </tr> * <tr> * <td style="width: 7em;">String</td> @@ -2232,24 +2303,30 @@ public final class ContactsContract { private Data() {} /** - * The content:// style URI for this table + * The content:// style URI for this table, which requests a directory + * of data rows matching the selection criteria. */ public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "data"); /** - * The MIME type of {@link #CONTENT_URI} providing a directory of data. + * The MIME type of the results from {@link #CONTENT_URI}. */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/data"; /** + * <p> * If {@link #FOR_EXPORT_ONLY} is explicitly set to "1", returned Cursor toward * Data.CONTENT_URI contains only exportable data. - * + * </p> + * <p> * This flag is useful (currently) only for vCard exporter in Contacts app, which * needs to exclude "un-exportable" data from available data to export, while * Contacts app itself has priviledge to access all data including "un-exportable" * ones and providers return all of them regardless of the callers' intention. - * <P>Type: INTEGER</p> + * </p> + * <p> + * Type: INTEGER + * </p> * * @hide Maybe available only in Eclair and not really ready for public use. * TODO: remove, or implement this feature completely. As of now (Eclair), @@ -2258,9 +2335,17 @@ public final class ContactsContract { public static final String FOR_EXPORT_ONLY = "for_export_only"; /** + * <p> * Build a {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} * style {@link Uri} for the parent {@link android.provider.ContactsContract.Contacts} * entry of the given {@link ContactsContract.Data} entry. + * </p> + * <p> + * Returns the Uri for the contact in the first entry returned by + * {@link ContentResolver#query(Uri, String[], String, String[], String)} + * for the provided {@code dataUri}. If the query returns null or empty + * results, silently returns null. + * </p> */ public static Uri getContactLookupUri(ContentResolver resolver, Uri dataUri) { final Cursor cursor = resolver.query(dataUri, new String[] { @@ -2283,7 +2368,7 @@ public final class ContactsContract { /** * <p> - * Constants for the raw contacts entities table, which can be though of as + * Constants for the raw contacts entities table, which can be thought of as * an outer join of the raw_contacts table with the data table. It is a strictly * read-only table. * </p> @@ -3039,7 +3124,7 @@ public final class ContactsContract { /** * The alphabet used for capturing the phonetic name. - * See {@link ContactsContract.PhoneticNameStyle}. + * See ContactsContract.PhoneticNameStyle. * @hide */ public static final String PHONETIC_NAME_STYLE = DATA11; @@ -3922,6 +4007,12 @@ public final class ContactsContract { * <td>{@link #DATA9}</td> * <td></td> * </tr> + * <tr> + * <td>String</td> + * <td>PHONETIC_NAME_STYLE</td> + * <td>{@link #DATA10}</td> + * <td></td> + * </tr> * </table> */ public static final class Organization implements DataColumnsWithJoins, CommonColumns { @@ -3979,6 +4070,13 @@ public final class ContactsContract { public static final String OFFICE_LOCATION = DATA9; /** + * The alphabet used for capturing the phonetic name. + * See {@link ContactsContract.PhoneticNameStyle}. + * @hide + */ + public static final String PHONETIC_NAME_STYLE = DATA10; + + /** * Return the string resource that best describes the given * {@link #TYPE}. Will always return a valid resource. */ @@ -4599,6 +4697,7 @@ public final class ContactsContract { super(cursor); } + @Override public Entity getEntityAndIncrementCursor(Cursor cursor) throws RemoteException { // we expect the cursor is already at the row we need to read from final ContentValues values = new ContentValues(); @@ -4881,8 +4980,10 @@ public final class ContactsContract { /** * Extra used to specify pivot dialog location in screen coordinates. + * @deprecated Use {@link Intent#setSourceBounds(Rect)} instead. * @hide */ + @Deprecated public static final String EXTRA_TARGET_RECT = "target_rect"; /** @@ -4942,15 +5043,17 @@ public final class ContactsContract { */ public static void showQuickContact(Context context, View target, Uri lookupUri, int mode, String[] excludeMimes) { - // Find location and bounds of target view - final int[] location = new int[2]; - target.getLocationOnScreen(location); + // Find location and bounds of target view, adjusting based on the + // assumed local density. + final float appScale = context.getResources().getCompatibilityInfo().applicationScale; + final int[] pos = new int[2]; + target.getLocationOnScreen(pos); final Rect rect = new Rect(); - rect.left = location[0]; - rect.top = location[1]; - rect.right = rect.left + target.getWidth(); - rect.bottom = rect.top + target.getHeight(); + rect.left = (int) (pos[0] * appScale + 0.5f); + rect.top = (int) (pos[1] * appScale + 0.5f); + rect.right = (int) ((pos[0] + target.getWidth()) * appScale + 0.5f); + rect.bottom = (int) ((pos[1] + target.getHeight()) * appScale + 0.5f); // Trigger with obtained rectangle showQuickContact(context, rect, lookupUri, mode, excludeMimes); @@ -4967,8 +5070,11 @@ public final class ContactsContract { * @param target Specific {@link Rect} that this dialog should be * centered around, in screen coordinates. In particular, if * the dialog has a "callout" arrow, it will be pointed and - * centered around this {@link Rect}. - * @param lookupUri A {@link ContactsContract.Contacts#CONTENT_LOOKUP_URI} style + * centered around this {@link Rect}. If you are running at a + * non-native density, you need to manually adjust using + * {@link DisplayMetrics#density} before calling. + * @param lookupUri A + * {@link ContactsContract.Contacts#CONTENT_LOOKUP_URI} style * {@link Uri} that describes a specific contact to feature * in this dialog. * @param mode Any of {@link #MODE_SMALL}, {@link #MODE_MEDIUM}, or @@ -4987,7 +5093,7 @@ public final class ContactsContract { | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); intent.setData(lookupUri); - intent.putExtra(EXTRA_TARGET_RECT, target); + intent.setSourceBounds(target); intent.putExtra(EXTRA_MODE, mode); intent.putExtra(EXTRA_EXCLUDE_MIMES, excludeMimes); context.startActivity(intent); diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index ae53dbe2..1b938ee 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -96,14 +96,12 @@ public final class MediaStore { /** * The name of an Intent-extra used to control the UI of a ViewImage. * This is a boolean property that overrides the activity's default fullscreen state. - * @hide */ public static final String EXTRA_FULL_SCREEN = "android.intent.extra.fullScreen"; /** * The name of an Intent-extra used to control the UI of a ViewImage. * This is a boolean property that specifies whether or not to show action icons. - * @hide */ public static final String EXTRA_SHOW_ACTION_ICONS = "android.intent.extra.showActionIcons"; @@ -162,13 +160,11 @@ public final class MediaStore { /** * Specify the maximum allowed size. - * @hide */ public final static String EXTRA_SIZE_LIMIT = "android.intent.extra.sizeLimit"; /** * Specify the maximum allowed recording duration in seconds. - * @hide */ public final static String EXTRA_DURATION_LIMIT = "android.intent.extra.durationLimit"; @@ -179,6 +175,13 @@ public final class MediaStore { public final static String EXTRA_OUTPUT = "output"; /** + * The string that is used when a media attribute is not known. For example, + * if an audio file does not have any meta data, the artist and album columns + * will be set to this value. + */ + public static final String UNKNOWN_STRING = "<unknown>"; + + /** * Common fields for most MediaProvider tables */ @@ -844,7 +847,6 @@ public final class MediaStore { * The position, in ms, playback was at when playback for this file * was last stopped. * <P>Type: INTEGER (long)</P> - * @hide */ public static final String BOOKMARK = "bookmark"; @@ -923,7 +925,6 @@ public final class MediaStore { /** * Non-zero if the audio file is a podcast * <P>Type: INTEGER (boolean)</P> - * @hide */ public static final String IS_PODCAST = "is_podcast"; @@ -964,7 +965,7 @@ public final class MediaStore { public static String keyFor(String name) { if (name != null) { boolean sortfirst = false; - if (name.equals(android.media.MediaFile.UNKNOWN_STRING)) { + if (name.equals(UNKNOWN_STRING)) { return "\001"; } // Check if the first character is \001. We use this to @@ -1258,7 +1259,6 @@ public final class MediaStore { * @param from The position of the item to move * @param to The position to move the item to * @return true on success - * @hide */ public static final boolean moveItem(ContentResolver res, long playlistId, int from, int to) { diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 8dc7a55..7db9fdc 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1453,7 +1453,6 @@ public final class Settings { */ public static final String[] SETTINGS_TO_BACKUP = { STAY_ON_WHILE_PLUGGED_IN, - END_BUTTON_BEHAVIOR, WIFI_SLEEP_POLICY, WIFI_USE_STATIC_IP, WIFI_STATIC_IP, @@ -2453,234 +2452,9 @@ public final class Settings { public static final String LAST_SETUP_SHOWN = "last_setup_shown"; /** - * @hide - */ - public static final String[] SETTINGS_TO_BACKUP = { - ADB_ENABLED, - ALLOW_MOCK_LOCATION, - PARENTAL_CONTROL_ENABLED, - PARENTAL_CONTROL_REDIRECT_URL, - USB_MASS_STORAGE_ENABLED, - ACCESSIBILITY_ENABLED, - ENABLED_ACCESSIBILITY_SERVICES, - TTS_USE_DEFAULTS, - TTS_DEFAULT_RATE, - TTS_DEFAULT_PITCH, - TTS_DEFAULT_SYNTH, - TTS_DEFAULT_LANG, - TTS_DEFAULT_COUNTRY, - WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, - WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, - WIFI_NUM_ALLOWED_CHANNELS, - WIFI_NUM_OPEN_NETWORKS_KEPT, - }; - - /** - * Helper method for determining if a location provider is enabled. - * @param cr the content resolver to use - * @param provider the location provider to query - * @return true if the provider is enabled - * - * @hide - */ - public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) { - String allowedProviders = Settings.Secure.getString(cr, LOCATION_PROVIDERS_ALLOWED); - if (allowedProviders != null) { - return (allowedProviders.equals(provider) || - allowedProviders.contains("," + provider + ",") || - allowedProviders.startsWith(provider + ",") || - allowedProviders.endsWith("," + provider)); - } - return false; - } - - /** - * Thread-safe method for enabling or disabling a single location provider. - * @param cr the content resolver to use - * @param provider the location provider to enable or disable - * @param enabled true if the provider should be enabled - * - * @hide - */ - public static final void setLocationProviderEnabled(ContentResolver cr, - String provider, boolean enabled) { - // to ensure thread safety, we write the provider name with a '+' or '-' - // and let the SettingsProvider handle it rather than reading and modifying - // the list of enabled providers. - if (enabled) { - provider = "+" + provider; - } else { - provider = "-" + provider; - } - putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider); - } - } - - /** - * Gservices settings, containing the network names for Google's - * various services. This table holds simple name/addr pairs. - * Addresses can be accessed through the getString() method. - * - * TODO: This should move to partner/google/... somewhere. - * - * @hide - */ - public static final class Gservices extends NameValueTable { - public static final String SYS_PROP_SETTING_VERSION = "sys.settings_gservices_version"; - - /** - * Intent action broadcast when the Gservices table is updated by the server. - * This is broadcast once after settings change (so many values may have been updated). - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String CHANGED_ACTION = - "com.google.gservices.intent.action.GSERVICES_CHANGED"; - - /** - * Intent action to override Gservices for testing. (Requires WRITE_GSERVICES permission.) - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String OVERRIDE_ACTION = - "com.google.gservices.intent.action.GSERVICES_OVERRIDE"; - - /** - * Intent action to set Gservices with new values. (Requires WRITE_GSERVICES permission.) - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String UPDATE_ACTION = - "com.google.gservices.intent.action.GSERVICES_UPDATE"; - - private static volatile NameValueCache mNameValueCache = null; - private static final Object mNameValueCacheLock = new Object(); - - /** - * Look up a name in the database. - * @param resolver to access the database with - * @param name to look up in the table - * @return the corresponding value, or null if not present - */ - public static String getString(ContentResolver resolver, String name) { - synchronized (mNameValueCacheLock) { - if (mNameValueCache == null) { - mNameValueCache = new NameValueCache(SYS_PROP_SETTING_VERSION, CONTENT_URI); - } - return mNameValueCache.getString(resolver, name); - } - } - - /** - * Store a name/value pair into the database. - * @param resolver to access the database with - * @param name to store - * @param value to associate with the name - * @return true if the value was set, false on database errors - */ - public static boolean putString(ContentResolver resolver, - String name, String value) { - return putString(resolver, CONTENT_URI, name, value); - } - - /** - * Look up the value for name in the database, convert it to an int using Integer.parseInt - * and return it. If it is null or if a NumberFormatException is caught during the - * conversion then return defValue. - */ - public static int getInt(ContentResolver resolver, String name, int defValue) { - String valString = getString(resolver, name); - int value; - try { - value = valString != null ? Integer.parseInt(valString) : defValue; - } catch (NumberFormatException e) { - value = defValue; - } - return value; - } - - /** - * Look up the value for name in the database, convert it to a long using Long.parseLong - * and return it. If it is null or if a NumberFormatException is caught during the - * conversion then return defValue. - */ - public static long getLong(ContentResolver resolver, String name, long defValue) { - String valString = getString(resolver, name); - long value; - try { - value = valString != null ? Long.parseLong(valString) : defValue; - } catch (NumberFormatException e) { - value = defValue; - } - return value; - } - - /** - * Construct the content URI for a particular name/value pair, - * useful for monitoring changes with a ContentObserver. - * @param name to look up in the table - * @return the corresponding content URI, or null if not present - */ - public static Uri getUriFor(String name) { - return getUriFor(CONTENT_URI, name); - } - - /** - * The content:// style URL for this table - */ - public static final Uri CONTENT_URI = - Uri.parse("content://" + AUTHORITY + "/gservices"); - - /** - * MMS - URL to use for HTTP "x-wap-profile" header - */ - public static final String MMS_X_WAP_PROFILE_URL - = "mms_x_wap_profile_url"; - - /** - * YouTube - the flag to indicate whether to use proxy - */ - public static final String YOUTUBE_USE_PROXY - = "youtube_use_proxy"; - - /** - * MMS - maximum message size in bytes for a MMS message. - */ - public static final String MMS_MAXIMUM_MESSAGE_SIZE - = "mms_maximum_message_size"; - - /** - * Event tags from the kernel event log to upload during checkin. - */ - public static final String CHECKIN_EVENTS = "checkin_events"; - - /** - * Comma-separated list of service names to dump and upload during checkin. - */ - public static final String CHECKIN_DUMPSYS_LIST = "checkin_dumpsys_list"; - - /** - * Comma-separated list of packages to specify for each service that is - * dumped (currently only meaningful for user activity). - */ - public static final String CHECKIN_PACKAGE_LIST = "checkin_package_list"; - - /** - * The interval (in seconds) between periodic checkin attempts. - */ - public static final String CHECKIN_INTERVAL = "checkin_interval"; - - /** - * The interval (in seconds) between event log aggregation runs. - */ - public static final String AGGREGATION_INTERVAL_SECONDS = "aggregation_interval_seconds"; - - /** - * Boolean indicating if the market app should force market only checkins on - * install/uninstall. Any non-0 value is considered true. - */ - public static final String MARKET_FORCE_CHECKIN = "market_force_checkin"; - - /** * How frequently (in seconds) to check the memory status of the * device. + * @hide */ public static final String MEMCHECK_INTERVAL = "memcheck_interval"; @@ -2688,6 +2462,7 @@ public final class Settings { * Max frequency (in seconds) to log memory check stats, in realtime * seconds. This allows for throttling of logs when the device is * running for large amounts of time. + * @hide */ public static final String MEMCHECK_LOG_REALTIME_INTERVAL = "memcheck_log_realtime_interval"; @@ -2695,6 +2470,7 @@ public final class Settings { /** * Boolean indicating whether rebooting due to system memory checks * is enabled. + * @hide */ public static final String MEMCHECK_SYSTEM_ENABLED = "memcheck_system_enabled"; @@ -2702,12 +2478,14 @@ public final class Settings { * How many bytes the system process must be below to avoid scheduling * a soft reboot. This reboot will happen when it is next determined * to be a good time. + * @hide */ public static final String MEMCHECK_SYSTEM_SOFT_THRESHOLD = "memcheck_system_soft"; /** * How many bytes the system process must be below to avoid scheduling * a hard reboot. This reboot will happen immediately. + * @hide */ public static final String MEMCHECK_SYSTEM_HARD_THRESHOLD = "memcheck_system_hard"; @@ -2715,18 +2493,21 @@ public final class Settings { * How many bytes the phone process must be below to avoid scheduling * a soft restart. This restart will happen when it is next determined * to be a good time. + * @hide */ public static final String MEMCHECK_PHONE_SOFT_THRESHOLD = "memcheck_phone_soft"; /** * How many bytes the phone process must be below to avoid scheduling * a hard restart. This restart will happen immediately. + * @hide */ public static final String MEMCHECK_PHONE_HARD_THRESHOLD = "memcheck_phone_hard"; /** * Boolean indicating whether restarting the phone process due to * memory checks is enabled. + * @hide */ public static final String MEMCHECK_PHONE_ENABLED = "memcheck_phone_enabled"; @@ -2734,6 +2515,7 @@ public final class Settings { * First time during the day it is okay to kill processes * or reboot the device due to low memory situations. This number is * in seconds since midnight. + * @hide */ public static final String MEMCHECK_EXEC_START_TIME = "memcheck_exec_start_time"; @@ -2741,6 +2523,7 @@ public final class Settings { * Last time during the day it is okay to kill processes * or reboot the device due to low memory situations. This number is * in seconds since midnight. + * @hide */ public static final String MEMCHECK_EXEC_END_TIME = "memcheck_exec_end_time"; @@ -2748,6 +2531,7 @@ public final class Settings { * How long the screen must have been off in order to kill processes * or reboot. This number is in seconds. A value of -1 means to * entirely disregard whether the screen is on. + * @hide */ public static final String MEMCHECK_MIN_SCREEN_OFF = "memcheck_min_screen_off"; @@ -2756,6 +2540,7 @@ public final class Settings { * or reboot. This number is in seconds. Note: this value must be * smaller than {@link #MEMCHECK_RECHECK_INTERVAL} or else it will * always see an alarm scheduled within its time. + * @hide */ public static final String MEMCHECK_MIN_ALARM = "memcheck_min_alarm"; @@ -2765,12 +2550,14 @@ public final class Settings { * this value must be larger than {@link #MEMCHECK_MIN_ALARM} or else * the alarm to schedule the recheck will always appear within the * minimum "do not execute now" time. + * @hide */ public static final String MEMCHECK_RECHECK_INTERVAL = "memcheck_recheck_interval"; /** * How frequently (in DAYS) to reboot the device. If 0, no reboots * will occur. + * @hide */ public static final String REBOOT_INTERVAL = "reboot_interval"; @@ -2778,6 +2565,7 @@ public final class Settings { * First time during the day it is okay to force a reboot of the * device (if REBOOT_INTERVAL is set). This number is * in seconds since midnight. + * @hide */ public static final String REBOOT_START_TIME = "reboot_start_time"; @@ -2786,692 +2574,117 @@ public final class Settings { * a reboot can be executed. If 0, a reboot will always be executed at * exactly the given time. Otherwise, it will only be executed if * the device is idle within the window. + * @hide */ public static final String REBOOT_WINDOW = "reboot_window"; /** - * The minimum version of the server that is required in order for the device to accept - * the server's recommendations about the initial sync settings to use. When this is unset, - * blank or can't be interpreted as an integer then we will not ask the server for a - * recommendation. - */ - public static final String GMAIL_CONFIG_INFO_MIN_SERVER_VERSION = - "gmail_config_info_min_server_version"; - - /** - * Controls whether Gmail offers a preview button for images. - */ - public static final String GMAIL_DISALLOW_IMAGE_PREVIEWS = "gmail_disallow_image_previews"; - - /** - * The maximal size in bytes allowed for attachments when composing messages in Gmail - */ - public static final String GMAIL_MAX_ATTACHMENT_SIZE = "gmail_max_attachment_size_bytes"; - - /** - * The timeout in milliseconds that Gmail uses when opening a connection and reading - * from it. A missing value or a value of -1 instructs Gmail to use the defaults provided - * by GoogleHttpClient. - */ - public static final String GMAIL_TIMEOUT_MS = "gmail_timeout_ms"; - - /** - * Controls whether Gmail will request an expedited sync when a message is sent. Value must - * be an integer where non-zero means true. Defaults to 1. - */ - public static final String GMAIL_SEND_IMMEDIATELY = "gmail_send_immediately"; - - /** - * Controls whether gmail buffers server responses. Possible values are "memory", for a - * memory-based buffer, or "file", for a temp-file-based buffer. All other values - * (including not set) disable buffering. - */ - public static final String GMAIL_BUFFER_SERVER_RESPONSE = "gmail_buffer_server_response"; - - /** - * The maximum size in bytes allowed for the provider to gzip a protocol buffer uploaded to - * the server. - */ - public static final String GMAIL_MAX_GZIP_SIZE = "gmail_max_gzip_size_bytes"; - - /** - * Controls whether Gmail will discard uphill operations that repeatedly fail. Value must be - * an integer where non-zero means true. Defaults to 1. This flag controls Donut devices. - */ - public static final String GMAIL_DISCARD_ERROR_UPHILL_OP = "gmail_discard_error_uphill_op"; - - /** - * Controls whether Gmail will discard uphill operations that repeatedly fail. Value must be - * an integer where non-zero means true. Defaults to 1. This flag controls Eclair and - * future devices. - */ - public static final String GMAIL_DISCARD_ERROR_UPHILL_OP_NEW = - "gmail_discard_error_uphill_op_new"; - - /** - * Controls how many attempts Gmail will try to upload an uphill operations before it - * abandons the operation. Defaults to 20. - */ - public static final String GMAIL_NUM_RETRY_UPHILL_OP = "gmail_num_retry_uphill_op"; - - /** - * How much time in seconds Gmail will try to upload an uphill operations before it - * abandons the operation. Defaults to 36400 (one day). - */ - public static final String GMAIL_WAIT_TIME_RETRY_UPHILL_OP = - "gmail_wait_time_retry_uphill_op"; - - /** - * Controls if Gmail should delay sending operations that have previously failed. - */ - public static final String GMAIL_DELAY_BAD_OP = "gmail_delay_bad_op"; - - /** - * Controls if the protocol buffer version of the protocol will use a multipart request for - * attachment uploads. Value must be an integer where non-zero means true. Defaults to 0. - */ - public static final String GMAIL_USE_MULTIPART_PROTOBUF = "gmail_use_multipart_protobuf"; - - /** - * the transcoder URL for mobile devices. - */ - public static final String TRANSCODER_URL = "mobile_transcoder_url"; - - /** - * URL that points to the privacy terms of the Google Talk service. - */ - public static final String GTALK_TERMS_OF_SERVICE_URL = "gtalk_terms_of_service_url"; - - /** - * Hostname of the GTalk server. - */ - public static final String GTALK_SERVICE_HOSTNAME = "gtalk_hostname"; - - /** - * Secure port of the GTalk server. - */ - public static final String GTALK_SERVICE_SECURE_PORT = "gtalk_secure_port"; - - /** - * The server configurable RMQ acking interval - */ - public static final String GTALK_SERVICE_RMQ_ACK_INTERVAL = "gtalk_rmq_ack_interval"; - - /** - * The minimum reconnect delay for short network outages or when the network is suspended - * due to phone use. - */ - public static final String GTALK_SERVICE_MIN_RECONNECT_DELAY_SHORT = - "gtalk_min_reconnect_delay_short"; - - /** - * The reconnect variant range for short network outages or when the network is suspended - * due to phone use. A random number between 0 and this constant is computed and - * added to {@link #GTALK_SERVICE_MIN_RECONNECT_DELAY_SHORT} to form the initial reconnect - * delay. - */ - public static final String GTALK_SERVICE_RECONNECT_VARIANT_SHORT = - "gtalk_reconnect_variant_short"; - - /** - * The minimum reconnect delay for long network outages - */ - public static final String GTALK_SERVICE_MIN_RECONNECT_DELAY_LONG = - "gtalk_min_reconnect_delay_long"; - - /** - * The reconnect variant range for long network outages. A random number between 0 and this - * constant is computed and added to {@link #GTALK_SERVICE_MIN_RECONNECT_DELAY_LONG} to - * form the initial reconnect delay. - */ - public static final String GTALK_SERVICE_RECONNECT_VARIANT_LONG = - "gtalk_reconnect_variant_long"; - - /** - * The maximum reconnect delay time, in milliseconds. - */ - public static final String GTALK_SERVICE_MAX_RECONNECT_DELAY = - "gtalk_max_reconnect_delay"; - - /** - * The network downtime that is considered "short" for the above calculations, - * in milliseconds. - */ - public static final String GTALK_SERVICE_SHORT_NETWORK_DOWNTIME = - "gtalk_short_network_downtime"; - - /** - * How frequently we send heartbeat pings to the GTalk server. Receiving a server packet - * will reset the heartbeat timer. The away heartbeat should be used when the user is - * logged into the GTalk app, but not actively using it. - */ - public static final String GTALK_SERVICE_AWAY_HEARTBEAT_INTERVAL_MS = - "gtalk_heartbeat_ping_interval_ms"; // keep the string backward compatible - - /** - * How frequently we send heartbeat pings to the GTalk server. Receiving a server packet - * will reset the heartbeat timer. The active heartbeat should be used when the user is - * actively using the GTalk app. - */ - public static final String GTALK_SERVICE_ACTIVE_HEARTBEAT_INTERVAL_MS = - "gtalk_active_heartbeat_ping_interval_ms"; - - /** - * How frequently we send heartbeat pings to the GTalk server. Receiving a server packet - * will reset the heartbeat timer. The sync heartbeat should be used when the user isn't - * logged into the GTalk app, but auto-sync is enabled. - */ - public static final String GTALK_SERVICE_SYNC_HEARTBEAT_INTERVAL_MS = - "gtalk_sync_heartbeat_ping_interval_ms"; - - /** - * How frequently we send heartbeat pings to the GTalk server. Receiving a server packet - * will reset the heartbeat timer. The no sync heartbeat should be used when the user isn't - * logged into the GTalk app, and auto-sync is not enabled. - */ - public static final String GTALK_SERVICE_NOSYNC_HEARTBEAT_INTERVAL_MS = - "gtalk_nosync_heartbeat_ping_interval_ms"; - - /** - * The maximum heartbeat interval used while on the WIFI network. - */ - public static final String GTALK_SERVICE_WIFI_MAX_HEARTBEAT_INTERVAL_MS = - "gtalk_wifi_max_heartbeat_ping_interval_ms"; - - /** - * How long we wait to receive a heartbeat ping acknowledgement (or another packet) - * from the GTalk server, before deeming the connection dead. - */ - public static final String GTALK_SERVICE_HEARTBEAT_ACK_TIMEOUT_MS = - "gtalk_heartbeat_ack_timeout_ms"; - - /** - * How long after screen is turned off before we consider the user to be idle. - */ - public static final String GTALK_SERVICE_IDLE_TIMEOUT_MS = - "gtalk_idle_timeout_ms"; - - /** - * By default, GTalkService will always connect to the server regardless of the auto-sync - * setting. However, if this parameter is true, then GTalkService will only connect - * if auto-sync is enabled. Using the GTalk app will trigger the connection too. - */ - public static final String GTALK_SERVICE_CONNECT_ON_AUTO_SYNC = - "gtalk_connect_on_auto_sync"; - - /** - * GTalkService holds a wakelock while broadcasting the intent for data message received. - * It then automatically release the wakelock after a timeout. This setting controls what - * the timeout should be. - */ - public static final String GTALK_DATA_MESSAGE_WAKELOCK_MS = - "gtalk_data_message_wakelock_ms"; - - /** - * The socket read timeout used to control how long ssl handshake wait for reads before - * timing out. This is needed so the ssl handshake doesn't hang for a long time in some - * circumstances. - */ - public static final String GTALK_SSL_HANDSHAKE_TIMEOUT_MS = - "gtalk_ssl_handshake_timeout_ms"; - - /** - * Compress the gtalk stream. - */ - public static final String GTALK_COMPRESS = "gtalk_compress"; - - /** - * This is the timeout for which Google Talk will send the message using bareJID. In a - * established chat between two XMPP endpoints, Google Talk uses fullJID in the format - * of user@domain/resource in order to send the message to the specific client. However, - * if Google Talk hasn't received a message from that client after some time, it would - * fall back to use the bareJID, which would broadcast the message to all clients for - * the other user. - */ - public static final String GTALK_USE_BARE_JID_TIMEOUT_MS = "gtalk_use_barejid_timeout_ms"; - - /** - * This is the threshold of retry number when there is an authentication expired failure - * for Google Talk. In some situation, e.g. when a Google Apps account is disabled chat - * service, the connection keeps failing. This threshold controls when we should stop - * the retrying. - */ - public static final String GTALK_MAX_RETRIES_FOR_AUTH_EXPIRED = - "gtalk_max_retries_for_auth_expired"; - - /** - * a boolean setting indicating whether the GTalkService should use RMQ2 protocol or not. - */ - public static final String GTALK_USE_RMQ2_PROTOCOL = - "gtalk_use_rmq2"; - - /** - * a boolean setting indicating whether the GTalkService should support both RMQ and - * RMQ2 protocols. This setting is true for the transitional period when we need to - * support both protocols. - */ - public static final String GTALK_SUPPORT_RMQ_AND_RMQ2_PROTOCOLS = - "gtalk_support_rmq_and_rmq2"; - - /** - * a boolean setting controlling whether the rmq2 protocol will include stream ids in - * the protobufs. This is used for debugging. - */ - public static final String GTALK_RMQ2_INCLUDE_STREAM_ID = - "gtalk_rmq2_include_stream_id"; - - /** - * when receiving a chat message from the server, the message could be an older message - * whose "time sent" is x seconds from now. If x is significant enough, we want to flag - * it so the UI can give it some special treatment when displaying the "time sent" for - * it. This setting is to control what x is. - */ - public static final String GTALK_OLD_CHAT_MESSAGE_THRESHOLD_IN_SEC = - "gtalk_old_chat_msg_threshold_in_sec"; - - /** - * a setting to control the max connection history record GTalkService stores. - */ - public static final String GTALK_MAX_CONNECTION_HISTORY_RECORDS = - "gtalk_max_conn_history_records"; - - /** - * This is gdata url to lookup album and picture info from picasa web. It also controls - * whether url scraping for picasa is enabled (NULL to disable). - */ - public static final String GTALK_PICASA_ALBUM_URL = - "gtalk_picasa_album_url"; - - /** - * This is the url to lookup picture info from flickr. It also controls - * whether url scraping for flickr is enabled (NULL to disable). - */ - public static final String GTALK_FLICKR_PHOTO_INFO_URL = - "gtalk_flickr_photo_info_url"; - - /** - * This is the url to lookup an actual picture from flickr. - */ - public static final String GTALK_FLICKR_PHOTO_URL = - "gtalk_flickr_photo_url"; - - /** - * This is the gdata url to lookup info on a youtube video. It also controls - * whether url scraping for youtube is enabled (NULL to disable). - */ - public static final String GTALK_YOUTUBE_VIDEO_URL = - "gtalk_youtube_video_url"; - - /** - * Enable/disable GTalk URL scraping for JPG images ("true" to enable). - */ - public static final String GTALK_URL_SCRAPING_FOR_JPG = - "gtalk_url_scraping_for_jpg"; - - /** - * Chat message lifetime (for pruning old chat messages). - */ - public static final String GTALK_CHAT_MESSAGE_LIFETIME = - "gtalk_chat_message_lifetime"; - - /** - * OTR message lifetime (for pruning old otr messages). - */ - public static final String GTALK_OTR_MESSAGE_LIFETIME = - "gtalk_otr_message_lifetime"; - - /** - * Chat expiration time, i.e., time since last message in the chat (for pruning old chats). - */ - public static final String GTALK_CHAT_EXPIRATION_TIME = - "gtalk_chat_expiration_time"; - - /** - * This is the url for getting the app token for server-to-device push messaging. - */ - public static final String PUSH_MESSAGING_REGISTRATION_URL = - "push_messaging_registration_url"; - - /** - * Use android://<it> routing infos for Google Sync Server subcriptions. - */ - public static final String GSYNC_USE_RMQ2_ROUTING_INFO = "gsync_use_rmq2_routing_info"; - - /** - * Enable use of ssl session caching. - * 'db' - save each session in a (per process) database - * 'file' - save each session in a (per process) file - * not set or any other value - normal java in-memory caching - */ - public static final String SSL_SESSION_CACHE = "ssl_session_cache"; - - /** - * How many bytes long a message has to be, in order to be gzipped. - */ - public static final String SYNC_MIN_GZIP_BYTES = - "sync_min_gzip_bytes"; - - /** - * The hash value of the current provisioning settings - */ - public static final String PROVISIONING_DIGEST = "digest"; - - /** - * Provisioning keys to block from server update - */ - public static final String PROVISIONING_OVERRIDE = "override"; - - /** - * "Generic" service name for authentication requests. - */ - public static final String GOOGLE_LOGIN_GENERIC_AUTH_SERVICE - = "google_login_generic_auth_service"; - - /** - * Duration in milliseconds after setup at which market does not reconcile applications - * which are installed during restore. - */ - public static final String VENDING_RESTORE_WINDOW_MS = "vending_restore_window_ms"; - - - /** - * Frequency in milliseconds at which we should sync the locally installed Vending Machine - * content with the server. - */ - public static final String VENDING_SYNC_FREQUENCY_MS = "vending_sync_frequency_ms"; - - /** - * Support URL that is opened in a browser when user clicks on 'Help and Info' in Vending - * Machine. - */ - public static final String VENDING_SUPPORT_URL = "vending_support_url"; - - /** - * Indicates if Vending Machine requires a SIM to be in the phone to allow a purchase. - * - * true = SIM is required - * false = SIM is not required - */ - public static final String VENDING_REQUIRE_SIM_FOR_PURCHASE = - "vending_require_sim_for_purchase"; - - /** - * Indicates the Vending Machine backup state. It is set if the - * Vending application has been backed up at least once. - */ - public static final String VENDING_BACKUP_STATE = "vending_backup_state"; - - /** - * The current version id of the Vending Machine terms of service. - */ - public static final String VENDING_TOS_VERSION = "vending_tos_version"; - - /** - * URL that points to the terms of service for Vending Machine. - */ - public static final String VENDING_TOS_URL = "vending_tos_url"; - - /** - * URL to navigate to in browser (not Market) when the terms of service - * for Vending Machine could not be accessed due to bad network - * connection. - */ - public static final String VENDING_TOS_MISSING_URL = "vending_tos_missing_url"; - - /** - * Whether to use sierraqa instead of sierra tokens for the purchase flow in - * Vending Machine. - * - * true = use sierraqa - * false = use sierra (default) - */ - public static final String VENDING_USE_CHECKOUT_QA_SERVICE = - "vending_use_checkout_qa_service"; - - /** - * Default value to use for all/free/priced filter in Market. - * Valid values: ALL, FREE, PAID (case insensitive) - */ - public static final String VENDING_DEFAULT_FILTER = "vending_default_filter"; - /** - * Ranking type value to use for the first category tab (currently popular) - */ - public static final String VENDING_TAB_1_RANKING_TYPE = "vending_tab_1_ranking_type"; - - /** - * Title string to use for first category tab. - */ - public static final String VENDING_TAB_1_TITLE = "vending_tab_1_title"; - - /** - * Ranking type value to use for the second category tab (currently newest) - */ - public static final String VENDING_TAB_2_RANKING_TYPE = "vending_tab_2_ranking_type"; - - /** - * Title string to use for second category tab. - */ - public static final String VENDING_TAB_2_TITLE = "vending_tab_2_title"; - - /** - * Frequency in milliseconds at which we should request MCS heartbeats - * from the Vending Machine client. - */ - public static final String VENDING_HEARTBEAT_FREQUENCY_MS = - "vending_heartbeat_frequency_ms"; - - /** - * Frequency in milliseconds at which we should resend pending download - * requests to the API Server from the Vending Machine client. - */ - public static final String VENDING_PENDING_DOWNLOAD_RESEND_FREQUENCY_MS = - "vending_pd_resend_frequency_ms"; - - /** - * Time before an asset in the 'DOWNLOADING' state is considered ready - * for an install kick on the client. - */ - public static final String VENDING_DOWNLOADING_KICK_TIMEOUT_MS = - "vending_downloading_kick_ms"; - - /** - * Size of buffer in bytes for Vending to use when reading cache files. - */ - public static final String VENDING_DISK_INPUT_BUFFER_BYTES = - "vending_disk_input_buffer_bytes"; - - /** - * Size of buffer in bytes for Vending to use when writing cache files. - */ - public static final String VENDING_DISK_OUTPUT_BUFFER_BYTES = - "vending_disk_output_buffer_bytes"; - - /** - * Frequency in milliseconds at which we should cycle through the promoted applications - * on the home screen or the categories page. - */ - public static final String VENDING_PROMO_REFRESH_FREQUENCY_MS = - "vending_promo_refresh_freq_ms"; - - /** - * Frequency in milliseconds when we should refresh the provisioning information from - * the carrier backend. - */ - public static final String VENDING_CARRIER_PROVISIONING_REFRESH_FREQUENCY_MS = - "vending_carrier_ref_freq_ms"; - - /** - * Interval in milliseconds after which a failed provisioning request should be retried. - */ - public static final String VENDING_CARRIER_PROVISIONING_RETRY_MS = - "vending_carrier_prov_retry_ms"; - - /** - * Buffer in milliseconds for carrier credentials to be considered valid. - */ - public static final String VENDING_CARRIER_CREDENTIALS_BUFFER_MS = - "vending_carrier_cred_buf_ms"; - - /** - * Whether to turn on performance logging in the Market client. - */ - public static final String VENDING_LOG_PERFORMANCE = - "vending_log_perf"; - - /** - * URL that points to the legal terms of service to display in Settings. - * <p> - * This should be a https URL. For a pretty user-friendly URL, use - * {@link #SETTINGS_TOS_PRETTY_URL}. - */ - public static final String SETTINGS_TOS_URL = "settings_tos_url"; - - /** - * URL that points to the legal terms of service to display in Settings. - * <p> - * This should be a pretty http URL. For the URL the device will access - * via Settings, use {@link #SETTINGS_TOS_URL}. - */ - public static final String SETTINGS_TOS_PRETTY_URL = "settings_tos_pretty_url"; - - /** - * URL that points to the contributors to display in Settings. - * <p> - * This should be a https URL. For a pretty user-friendly URL, use - * {@link #SETTINGS_CONTRIBUTORS_PRETTY_URL}. - */ - public static final String SETTINGS_CONTRIBUTORS_URL = "settings_contributors_url"; - - /** - * URL that points to the contributors to display in Settings. - * <p> - * This should be a pretty http URL. For the URL the device will access - * via Settings, use {@link #SETTINGS_CONTRIBUTORS_URL}. + * Threshold values for the duration and level of a discharge cycle, under + * which we log discharge cycle info. + * @hide */ - public static final String SETTINGS_CONTRIBUTORS_PRETTY_URL = - "settings_contributors_pretty_url"; + public static final String BATTERY_DISCHARGE_DURATION_THRESHOLD = + "battery_discharge_duration_threshold"; + /** @hide */ + public static final String BATTERY_DISCHARGE_THRESHOLD = "battery_discharge_threshold"; /** - * URL that points to the Terms Of Service for the device. - * <p> - * This should be a pretty http URL. + * Flag for allowing ActivityManagerService to send ACTION_APP_ERROR intents + * on application crashes and ANRs. If this is disabled, the crash/ANR dialog + * will never display the "Report" button. + * Type: int ( 0 = disallow, 1 = allow ) + * @hide */ - public static final String SETUP_GOOGLE_TOS_URL = "setup_google_tos_url"; + public static final String SEND_ACTION_APP_ERROR = "send_action_app_error"; /** - * URL that points to the Android privacy policy for the device. - * <p> - * This should be a pretty http URL. + * Nonzero causes Log.wtf() to crash. + * @hide */ - public static final String SETUP_ANDROID_PRIVACY_URL = "setup_android_privacy_url"; + public static final String WTF_IS_FATAL = "wtf_is_fatal"; /** - * URL that points to the Google privacy policy for the device. - * <p> - * This should be a pretty http URL. + * Maximum age of entries kept by {@link android.os.IDropBox}. + * @hide */ - public static final String SETUP_GOOGLE_PRIVACY_URL = "setup_google_privacy_url"; - + public static final String DROPBOX_AGE_SECONDS = + "dropbox_age_seconds"; /** - * Request an MSISDN token for various Google services. + * Maximum amount of disk space used by {@link android.os.IDropBox} no matter what. + * @hide */ - public static final String USE_MSISDN_TOKEN = "use_msisdn_token"; - + public static final String DROPBOX_QUOTA_KB = + "dropbox_quota_kb"; /** - * RSA public key used to encrypt passwords stored in the database. + * Percent of free disk (excluding reserve) which {@link android.os.IDropBox} will use. + * @hide */ - public static final String GLS_PUBLIC_KEY = "google_login_public_key"; - + public static final String DROPBOX_QUOTA_PERCENT = + "dropbox_quota_percent"; /** - * Only check parental control status if this is set to "true". + * Percent of total disk which {@link android.os.IDropBox} will never dip into. + * @hide */ - public static final String PARENTAL_CONTROL_CHECK_ENABLED = - "parental_control_check_enabled"; - + public static final String DROPBOX_RESERVE_PERCENT = + "dropbox_reserve_percent"; /** - * The list of applications we need to block if parental control is - * enabled. + * Prefix for per-tag dropbox disable/enable settings. + * @hide */ - public static final String PARENTAL_CONTROL_APPS_LIST = - "parental_control_apps_list"; + public static final String DROPBOX_TAG_PREFIX = + "dropbox:"; - /** - * Duration in which parental control status is valid. - */ - public static final String PARENTAL_CONTROL_TIMEOUT_IN_MS = - "parental_control_timeout_in_ms"; /** - * When parental control is off, we expect to get this string from the - * litmus url. + * Screen timeout in milliseconds corresponding to the + * PowerManager's POKE_LOCK_SHORT_TIMEOUT flag (i.e. the fastest + * possible screen timeout behavior.) + * @hide */ - public static final String PARENTAL_CONTROL_EXPECTED_RESPONSE = - "parental_control_expected_response"; + public static final String SHORT_KEYLIGHT_DELAY_MS = + "short_keylight_delay_ms"; /** - * When the litmus url returns a 302, declare parental control to be on - * only if the redirect url matches this regular expression. + * The interval in minutes after which the amount of free storage left on the + * device is logged to the event log + * @hide */ - public static final String PARENTAL_CONTROL_REDIRECT_REGEX = - "parental_control_redirect_regex"; + public static final String SYS_FREE_STORAGE_LOG_INTERVAL = + "sys_free_storage_log_interval"; /** * Threshold for the amount of change in disk free space required to report the amount of * free space. Used to prevent spamming the logs when the disk free space isn't changing * frequently. + * @hide */ public static final String DISK_FREE_CHANGE_REPORTING_THRESHOLD = "disk_free_change_reporting_threshold"; - /** - * Prefix for new Google services published by the checkin - * server. - */ - public static final String GOOGLE_SERVICES_PREFIX - = "google_services:"; - - /** - * The maximum reconnect delay for short network outages or when the network is suspended - * due to phone use. - */ - public static final String SYNC_MAX_RETRY_DELAY_IN_SECONDS = - "sync_max_retry_delay_in_seconds"; /** * Minimum percentage of free storage on the device that is used to determine if * the device is running low on storage. * Say this value is set to 10, the device is considered running low on storage * if 90% or more of the device storage is filled up. + * @hide */ public static final String SYS_STORAGE_THRESHOLD_PERCENTAGE = "sys_storage_threshold_percentage"; /** - * The interval in minutes after which the amount of free storage left on the - * device is logged to the event log - */ - public static final String SYS_FREE_STORAGE_LOG_INTERVAL = - "sys_free_storage_log_interval"; - - /** - * The interval in milliseconds at which to check the number of SMS sent - * out without asking for use permit, to limit the un-authorized SMS - * usage. - */ - public static final String SMS_OUTGOING_CHECK_INTERVAL_MS = - "sms_outgoing_check_interval_ms"; - - /** - * The number of outgoing SMS sent without asking for user permit - * (of {@link #SMS_OUTGOING_CHECK_INTERVAL_MS} + * The interval in milliseconds after which Wi-Fi is considered idle. + * When idle, it is possible for the device to be switched from Wi-Fi to + * the mobile data network. + * @hide */ - public static final String SMS_OUTGOING_CEHCK_MAX_COUNT = - "sms_outgoing_check_max_count"; + public static final String WIFI_IDLE_MS = "wifi_idle_ms"; /** * The interval in milliseconds at which to check packet counts on the * mobile data interface when screen is on, to detect possible data * connection problems. + * @hide */ public static final String PDP_WATCHDOG_POLL_INTERVAL_MS = "pdp_watchdog_poll_interval_ms"; @@ -3480,6 +2693,7 @@ public final class Settings { * The interval in milliseconds at which to check packet counts on the * mobile data interface when screen is off, to detect possible data * connection problems. + * @hide */ public static final String PDP_WATCHDOG_LONG_POLL_INTERVAL_MS = "pdp_watchdog_long_poll_interval_ms"; @@ -3488,6 +2702,7 @@ public final class Settings { * The interval in milliseconds at which to check packet counts on the * mobile data interface after {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT} * outgoing packets has been reached without incoming packets. + * @hide */ public static final String PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS = "pdp_watchdog_error_poll_interval_ms"; @@ -3496,6 +2711,7 @@ public final class Settings { * The number of outgoing packets sent without seeing an incoming packet * that triggers a countdown (of {@link #PDP_WATCHDOG_ERROR_POLL_COUNT} * device is logged to the event log + * @hide */ public static final String PDP_WATCHDOG_TRIGGER_PACKET_COUNT = "pdp_watchdog_trigger_packet_count"; @@ -3504,6 +2720,7 @@ public final class Settings { * The number of polls to perform (at {@link #PDP_WATCHDOG_ERROR_POLL_INTERVAL_MS}) * after hitting {@link #PDP_WATCHDOG_TRIGGER_PACKET_COUNT} before * attempting data connection recovery. + * @hide */ public static final String PDP_WATCHDOG_ERROR_POLL_COUNT = "pdp_watchdog_error_poll_count"; @@ -3511,6 +2728,7 @@ public final class Settings { /** * The number of failed PDP reset attempts before moving to something more * drastic: re-registering to the network. + * @hide */ public static final String PDP_WATCHDOG_MAX_PDP_RESET_FAIL_COUNT = "pdp_watchdog_max_pdp_reset_fail_count"; @@ -3518,12 +2736,14 @@ public final class Settings { /** * Address to ping as a last sanity check before attempting any recovery. * Unset or set to "0.0.0.0" to skip this check. + * @hide */ public static final String PDP_WATCHDOG_PING_ADDRESS = "pdp_watchdog_ping_address"; /** * The "-w deadline" parameter for the ping, ie, the max time in * seconds to spend pinging. + * @hide */ public static final String PDP_WATCHDOG_PING_DEADLINE = "pdp_watchdog_ping_deadline"; @@ -3532,231 +2752,269 @@ public final class Settings { * after the first registration mismatch of gprs and voice service, * to detect possible data network registration problems. * + * @hide */ public static final String GPRS_REGISTER_CHECK_PERIOD_MS = "gprs_register_check_period_ms"; /** - * The interval in milliseconds after which Wi-Fi is considered idle. - * When idle, it is possible for the device to be switched from Wi-Fi to - * the mobile data network. - */ - public static final String WIFI_IDLE_MS = "wifi_idle_ms"; - - /** - * Screen timeout in milliseconds corresponding to the - * PowerManager's POKE_LOCK_SHORT_TIMEOUT flag (i.e. the fastest - * possible screen timeout behavior.) + * The length of time in milli-seconds that automatic small adjustments to + * SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded. + * @hide */ - public static final String SHORT_KEYLIGHT_DELAY_MS = - "short_keylight_delay_ms"; + public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing"; /** - * List of test suites (local disk filename) for the automatic instrumentation test runner. - * The file format is similar to automated_suites.xml, see AutoTesterService. - * If this setting is missing or empty, the automatic test runner will not start. + * If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment + * to SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been + * exceeded. + * @hide */ - public static final String AUTOTEST_SUITES_FILE = "autotest_suites_file"; + public static final String NITZ_UPDATE_DIFF = "nitz_update_diff"; /** - * Interval between synchronous checkins forced by the automatic test runner. - * If you set this to a value smaller than CHECKIN_INTERVAL, then the test runner's - * frequent checkins will prevent asynchronous background checkins from interfering - * with any performance measurements. + * The maximum reconnect delay for short network outages or when the network is suspended + * due to phone use. + * @hide */ - public static final String AUTOTEST_CHECKIN_SECONDS = "autotest_checkin_seconds"; + public static final String SYNC_MAX_RETRY_DELAY_IN_SECONDS = + "sync_max_retry_delay_in_seconds"; /** - * Interval between reboots forced by the automatic test runner. + * The interval in milliseconds at which to check the number of SMS sent + * out without asking for use permit, to limit the un-authorized SMS + * usage. + * @hide */ - public static final String AUTOTEST_REBOOT_SECONDS = "autotest_reboot_seconds"; - + public static final String SMS_OUTGOING_CHECK_INTERVAL_MS = + "sms_outgoing_check_interval_ms"; /** - * Threshold values for the duration and level of a discharge cycle, under - * which we log discharge cycle info. + * The number of outgoing SMS sent without asking for user permit + * (of {@link #SMS_OUTGOING_CHECK_INTERVAL_MS} + * @hide */ - public static final String BATTERY_DISCHARGE_DURATION_THRESHOLD = - "battery_discharge_duration_threshold"; - public static final String BATTERY_DISCHARGE_THRESHOLD = "battery_discharge_threshold"; + public static final String SMS_OUTGOING_CHECK_MAX_COUNT = + "sms_outgoing_check_max_count"; /** - * An email address that anr bugreports should be sent to. + * Enable use of ssl session caching. + * 'db' - save each session in a (per process) database + * 'file' - save each session in a (per process) file + * not set or any other value - normal java in-memory caching + * @hide */ - public static final String ANR_BUGREPORT_RECIPIENT = "anr_bugreport_recipient"; + public static final String SSL_SESSION_CACHE = "ssl_session_cache"; /** - * Flag for allowing service provider to use location information to improve products and - * services. - * Type: int ( 0 = disallow, 1 = allow ) - * @deprecated + * How many bytes long a message has to be, in order to be gzipped. + * @hide */ - public static final String USE_LOCATION_FOR_SERVICES = "use_location"; + public static final String SYNC_MIN_GZIP_BYTES = + "sync_min_gzip_bytes"; /** * The number of promoted sources in GlobalSearch. + * @hide */ public static final String SEARCH_NUM_PROMOTED_SOURCES = "search_num_promoted_sources"; /** * The maximum number of suggestions returned by GlobalSearch. + * @hide */ public static final String SEARCH_MAX_RESULTS_TO_DISPLAY = "search_max_results_to_display"; /** * The number of suggestions GlobalSearch will ask each non-web search source for. + * @hide */ public static final String SEARCH_MAX_RESULTS_PER_SOURCE = "search_max_results_per_source"; /** * The number of suggestions the GlobalSearch will ask the web search source for. + * @hide */ public static final String SEARCH_WEB_RESULTS_OVERRIDE_LIMIT = "search_web_results_override_limit"; /** * The number of milliseconds that GlobalSearch will wait for suggestions from * promoted sources before continuing with all other sources. + * @hide */ public static final String SEARCH_PROMOTED_SOURCE_DEADLINE_MILLIS = "search_promoted_source_deadline_millis"; /** * The number of milliseconds before GlobalSearch aborts search suggesiton queries. + * @hide */ public static final String SEARCH_SOURCE_TIMEOUT_MILLIS = "search_source_timeout_millis"; /** * The maximum number of milliseconds that GlobalSearch shows the previous results * after receiving a new query. + * @hide */ public static final String SEARCH_PREFILL_MILLIS = "search_prefill_millis"; /** * The maximum age of log data used for shortcuts in GlobalSearch. + * @hide */ public static final String SEARCH_MAX_STAT_AGE_MILLIS = "search_max_stat_age_millis"; /** * The maximum age of log data used for source ranking in GlobalSearch. + * @hide */ public static final String SEARCH_MAX_SOURCE_EVENT_AGE_MILLIS = "search_max_source_event_age_millis"; /** * The minimum number of impressions needed to rank a source in GlobalSearch. + * @hide */ public static final String SEARCH_MIN_IMPRESSIONS_FOR_SOURCE_RANKING = "search_min_impressions_for_source_ranking"; /** * The minimum number of clicks needed to rank a source in GlobalSearch. + * @hide */ public static final String SEARCH_MIN_CLICKS_FOR_SOURCE_RANKING = "search_min_clicks_for_source_ranking"; /** * The maximum number of shortcuts shown by GlobalSearch. + * @hide */ public static final String SEARCH_MAX_SHORTCUTS_RETURNED = "search_max_shortcuts_returned"; /** * The size of the core thread pool for suggestion queries in GlobalSearch. + * @hide */ public static final String SEARCH_QUERY_THREAD_CORE_POOL_SIZE = "search_query_thread_core_pool_size"; /** * The maximum size of the thread pool for suggestion queries in GlobalSearch. + * @hide */ public static final String SEARCH_QUERY_THREAD_MAX_POOL_SIZE = "search_query_thread_max_pool_size"; /** * The size of the core thread pool for shortcut refreshing in GlobalSearch. + * @hide */ public static final String SEARCH_SHORTCUT_REFRESH_CORE_POOL_SIZE = "search_shortcut_refresh_core_pool_size"; /** * The maximum size of the thread pool for shortcut refreshing in GlobalSearch. + * @hide */ public static final String SEARCH_SHORTCUT_REFRESH_MAX_POOL_SIZE = "search_shortcut_refresh_max_pool_size"; /** * The maximun time that excess threads in the GlobalSeach thread pools will * wait before terminating. + * @hide */ public static final String SEARCH_THREAD_KEEPALIVE_SECONDS = "search_thread_keepalive_seconds"; /** * The maximum number of concurrent suggestion queries to each source. + * @hide */ public static final String SEARCH_PER_SOURCE_CONCURRENT_QUERY_LIMIT = "search_per_source_concurrent_query_limit"; - /** - * Flag for allowing ActivityManagerService to send ACTION_APP_ERROR intents - * on application crashes and ANRs. If this is disabled, the crash/ANR dialog - * will never display the "Report" button. - * Type: int ( 0 = disallow, 1 = allow ) - */ - public static final String SEND_ACTION_APP_ERROR = "send_action_app_error"; /** - * Maximum size of /proc/last_kmsg content to upload after reboot. + * Whether or not alert sounds are played on MountService events. (0 = false, 1 = true) + * @hide */ - public static final String LAST_KMSG_KB = "last_kmsg_kb"; + public static final String MOUNT_PLAY_NOTIFICATION_SND = "mount_play_not_snd"; /** - * Maximum age of entries kept by {@link android.os.IDropBox}. - */ - public static final String DROPBOX_AGE_SECONDS = - "dropbox_age_seconds"; - /** - * Maximum amount of disk space used by {@link android.os.IDropBox} no matter what. - */ - public static final String DROPBOX_QUOTA_KB = - "dropbox_quota_kb"; - /** - * Percent of free disk (excluding reserve) which {@link android.os.IDropBox} will use. + * Whether or not UMS auto-starts on UMS host detection. (0 = false, 1 = true) + * @hide */ - public static final String DROPBOX_QUOTA_PERCENT = - "dropbox_quota_percent"; + public static final String MOUNT_UMS_AUTOSTART = "mount_ums_autostart"; + /** - * Percent of total disk which {@link android.os.IDropBox} will never dip into. + * Whether or not a notification is displayed on UMS host detection. (0 = false, 1 = true) + * @hide */ - public static final String DROPBOX_RESERVE_PERCENT = - "dropbox_reserve_percent"; + public static final String MOUNT_UMS_PROMPT = "mount_ums_prompt"; + /** - * Prefix for per-tag dropbox disable/enable settings. + * Whether or not a notification is displayed while UMS is enabled. (0 = false, 1 = true) + * @hide */ - public static final String DROPBOX_TAG_PREFIX = - "dropbox:"; + public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled"; /** - * The length of time in milli-seconds that automatic small adjustments to - * SystemClock are ignored if NITZ_UPDATE_DIFF is not exceeded. + * If nonzero, ANRs in invisible background processes bring up a dialog. + * Otherwise, the process will be silently killed. + * @hide */ - public static final String NITZ_UPDATE_SPACING = "nitz_update_spacing"; + public static final String ANR_SHOW_BACKGROUND = "anr_show_background"; /** - * If the NITZ_UPDATE_DIFF time is exceeded then an automatic adjustment - * to SystemClock will be allowed even if NITZ_UPDATE_SPACING has not been - * exceeded. + * @hide */ - public static final String NITZ_UPDATE_DIFF = "nitz_update_diff"; + public static final String[] SETTINGS_TO_BACKUP = { + ADB_ENABLED, + ALLOW_MOCK_LOCATION, + PARENTAL_CONTROL_ENABLED, + PARENTAL_CONTROL_REDIRECT_URL, + USB_MASS_STORAGE_ENABLED, + ACCESSIBILITY_ENABLED, + ENABLED_ACCESSIBILITY_SERVICES, + TTS_USE_DEFAULTS, + TTS_DEFAULT_RATE, + TTS_DEFAULT_PITCH, + TTS_DEFAULT_SYNTH, + TTS_DEFAULT_LANG, + TTS_DEFAULT_COUNTRY, + WIFI_NETWORKS_AVAILABLE_NOTIFICATION_ON, + WIFI_NETWORKS_AVAILABLE_REPEAT_DELAY, + WIFI_NUM_ALLOWED_CHANNELS, + WIFI_NUM_OPEN_NETWORKS_KEPT, + MOUNT_PLAY_NOTIFICATION_SND, + MOUNT_UMS_AUTOSTART, + MOUNT_UMS_PROMPT, + MOUNT_UMS_NOTIFY_ENABLED + }; /** - * @deprecated + * Helper method for determining if a location provider is enabled. + * @param cr the content resolver to use + * @param provider the location provider to query + * @return true if the provider is enabled + * * @hide */ - @Deprecated // Obviated by NameValueCache: just fetch the value directly. - public static class QueryMap extends ContentQueryMap { - - public QueryMap(ContentResolver contentResolver, Cursor cursor, boolean keepUpdated, - Handler handlerForUpdateNotifications) { - super(cursor, NAME, keepUpdated, handlerForUpdateNotifications); - } - - public QueryMap(ContentResolver contentResolver, boolean keepUpdated, - Handler handlerForUpdateNotifications) { - this(contentResolver, - contentResolver.query(CONTENT_URI, null, null, null, null), - keepUpdated, handlerForUpdateNotifications); + public static final boolean isLocationProviderEnabled(ContentResolver cr, String provider) { + String allowedProviders = Settings.Secure.getString(cr, LOCATION_PROVIDERS_ALLOWED); + if (allowedProviders != null) { + return (allowedProviders.equals(provider) || + allowedProviders.contains("," + provider + ",") || + allowedProviders.startsWith(provider + ",") || + allowedProviders.endsWith("," + provider)); } + return false; + } - public String getString(String name) { - ContentValues cv = getValues(name); - if (cv == null) return null; - return cv.getAsString(VALUE); + /** + * Thread-safe method for enabling or disabling a single location provider. + * @param cr the content resolver to use + * @param provider the location provider to enable or disable + * @param enabled true if the provider should be enabled + * + * @hide + */ + public static final void setLocationProviderEnabled(ContentResolver cr, + String provider, boolean enabled) { + // to ensure thread safety, we write the provider name with a '+' or '-' + // and let the SettingsProvider handle it rather than reading and modifying + // the list of enabled providers. + if (enabled) { + provider = "+" + provider; + } else { + provider = "-" + provider; } + putString(cr, Settings.Secure.LOCATION_PROVIDERS_ALLOWED, provider); } - } /** diff --git a/core/java/android/server/BluetoothA2dpService.java b/core/java/android/server/BluetoothA2dpService.java index 03dcf00..22bb43c 100644 --- a/core/java/android/server/BluetoothA2dpService.java +++ b/core/java/android/server/BluetoothA2dpService.java @@ -96,11 +96,12 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { BluetoothDevice.ERROR); switch(bondState) { case BluetoothDevice.BOND_BONDED: - setSinkPriority(device, BluetoothA2dp.PRIORITY_ON); + if (getSinkPriority(device) == BluetoothA2dp.PRIORITY_UNDEFINED) { + setSinkPriority(device, BluetoothA2dp.PRIORITY_ON); + } break; - case BluetoothDevice.BOND_BONDING: case BluetoothDevice.BOND_NONE: - setSinkPriority(device, BluetoothA2dp.PRIORITY_OFF); + setSinkPriority(device, BluetoothA2dp.PRIORITY_UNDEFINED); break; } } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { @@ -405,7 +406,7 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); return Settings.Secure.getInt(mContext.getContentResolver(), Settings.Secure.getBluetoothA2dpSinkPriorityKey(device.getAddress()), - BluetoothA2dp.PRIORITY_OFF); + BluetoothA2dp.PRIORITY_UNDEFINED); } public synchronized boolean setSinkPriority(BluetoothDevice device, int priority) { @@ -464,11 +465,8 @@ public class BluetoothA2dpService extends IBluetoothA2dp.Stub { checkSinkSuspendState(state); mTargetA2dpState = -1; - if (state == BluetoothA2dp.STATE_CONNECTING) { - mAudioManager.setParameters("A2dpSuspended=false"); - } - - if (state == BluetoothA2dp.STATE_CONNECTING || + if (getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF && + state == BluetoothA2dp.STATE_CONNECTING || state == BluetoothA2dp.STATE_CONNECTED) { // We have connected or attempting to connect. // Bump priority diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 0d0d245..b28cf43 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -21,14 +21,15 @@ import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothClass; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothUuid; -import android.os.ParcelUuid; import android.content.Context; import android.content.Intent; import android.os.Handler; import android.os.Message; +import android.os.ParcelUuid; import android.util.Log; import java.util.HashMap; +import java.util.Set; /** * TODO: Move this to @@ -553,7 +554,7 @@ class BluetoothEventLoop { if (mBluetoothService.isEnabled() && (BluetoothUuid.isAudioSource(uuid) || BluetoothUuid.isAvrcpTarget(uuid) || BluetoothUuid.isAdvAudioDist(uuid)) && - (a2dp.getNonDisconnectedSinks().size() == 0)) { + !isOtherSinkInNonDisconnectingState(address)) { BluetoothDevice device = mAdapter.getRemoteDevice(address); authorized = a2dp.getSinkPriority(device) > BluetoothA2dp.PRIORITY_OFF; if (authorized) { @@ -568,6 +569,16 @@ class BluetoothEventLoop { return authorized; } + boolean isOtherSinkInNonDisconnectingState(String address) { + BluetoothA2dp a2dp = new BluetoothA2dp(mContext); + Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks(); + if (devices.size() == 0) return false; + for(BluetoothDevice dev: devices) { + if (!dev.getAddress().equals(address)) return true; + } + return false; + } + private void onAgentCancel() { Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_CANCEL); mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM); diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index b590449..dfb775f 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -74,6 +74,7 @@ public class BluetoothService extends IBluetooth.Stub { private int mNativeData; private BluetoothEventLoop mEventLoop; private boolean mIsAirplaneSensitive; + private boolean mIsAirplaneToggleable; private int mBluetoothState; private boolean mRestart = false; // need to call enable() after disable() private boolean mIsDiscovering; @@ -370,7 +371,7 @@ public class BluetoothService extends IBluetooth.Stub { "Need BLUETOOTH_ADMIN permission"); // Airplane mode can prevent Bluetooth radio from being turned on. - if (mIsAirplaneSensitive && isAirplaneModeOn()) { + if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) { return false; } if (mBluetoothState != BluetoothAdapter.STATE_OFF) { @@ -545,7 +546,7 @@ public class BluetoothService extends IBluetooth.Stub { mEventLoop.onPropertyChanged(propVal); } - if (mIsAirplaneSensitive && isAirplaneModeOn()) { + if (mIsAirplaneSensitive && isAirplaneModeOn() && !mIsAirplaneToggleable) { disable(false); } @@ -1597,10 +1598,17 @@ public class BluetoothService extends IBluetooth.Stub { }; private void registerForAirplaneMode(IntentFilter filter) { - String airplaneModeRadios = Settings.System.getString(mContext.getContentResolver(), + final ContentResolver resolver = mContext.getContentResolver(); + final String airplaneModeRadios = Settings.System.getString(resolver, Settings.System.AIRPLANE_MODE_RADIOS); - mIsAirplaneSensitive = airplaneModeRadios == null - ? true : airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH); + final String toggleableRadios = Settings.System.getString(resolver, + Settings.System.AIRPLANE_MODE_TOGGLEABLE_RADIOS); + + mIsAirplaneSensitive = airplaneModeRadios == null ? true : + airplaneModeRadios.contains(Settings.System.RADIO_BLUETOOTH); + mIsAirplaneToggleable = toggleableRadios == null ? false : + toggleableRadios.contains(Settings.System.RADIO_BLUETOOTH); + if (mIsAirplaneSensitive) { filter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED); } @@ -1661,6 +1669,7 @@ public class BluetoothService extends IBluetooth.Stub { } pw.println("mIsAirplaneSensitive = " + mIsAirplaneSensitive); + pw.println("mIsAirplaneToggleable = " + mIsAirplaneToggleable); pw.println("Local address = " + getAddress()); pw.println("Local name = " + getName()); diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java index 45719e4..fe3b149 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -434,9 +434,9 @@ public abstract class WallpaperService extends Service { } int myWidth = mSurfaceHolder.getRequestedWidth(); - if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.FILL_PARENT; + if (myWidth <= 0) myWidth = ViewGroup.LayoutParams.MATCH_PARENT; int myHeight = mSurfaceHolder.getRequestedHeight(); - if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.FILL_PARENT; + if (myHeight <= 0) myHeight = ViewGroup.LayoutParams.MATCH_PARENT; final boolean creating = !mCreated; final boolean formatChanged = mFormat != mSurfaceHolder.getRequestedFormat(); diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl index 1812188..2ed660a 100755 --- a/core/java/android/speech/tts/ITts.aidl +++ b/core/java/android/speech/tts/ITts.aidl @@ -59,5 +59,7 @@ interface ITts { int unregisterCallback(in String callingApp, ITtsCallback cb);
- int playSilence(in String callingApp, in long duration, in int queueMode, in String[] params);
+ int playSilence(in String callingApp, in long duration, in int queueMode, in String[] params); +
+ int setEngineByPackageName(in String enginePackageName);
}
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 3f369dd..bbbeb3f 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -129,8 +129,8 @@ public class TextToSpeech { * {@link TextToSpeech#synthesizeToFile(String, HashMap, String)} with the * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID} key. * @param utteranceId the identifier of the utterance. - */
- public void onUtteranceCompleted(String utteranceId);
+ */ + public void onUtteranceCompleted(String utteranceId); } @@ -286,6 +286,10 @@ public class TextToSpeech { */ public static final String KEY_PARAM_VARIANT = "variant"; /** + * {@hide} + */ + public static final String KEY_PARAM_ENGINE = "engine"; + /** * Parameter key to specify the audio stream type to be used when speaking text * or playing back a file. * @see TextToSpeech#speak(String, int, HashMap) @@ -327,10 +331,16 @@ public class TextToSpeech { * {@hide} */ protected static final int PARAM_POSITION_UTTERANCE_ID = 10; + + /** + * {@hide} + */ + protected static final int PARAM_POSITION_ENGINE = 12; + /** * {@hide} */ - protected static final int NB_CACHED_PARAMS = 6; + protected static final int NB_CACHED_PARAMS = 7; } /** @@ -373,6 +383,7 @@ public class TextToSpeech { mCachedParams[Engine.PARAM_POSITION_VARIANT] = Engine.KEY_PARAM_VARIANT; mCachedParams[Engine.PARAM_POSITION_STREAM] = Engine.KEY_PARAM_STREAM; mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID] = Engine.KEY_PARAM_UTTERANCE_ID; + mCachedParams[Engine.PARAM_POSITION_ENGINE] = Engine.KEY_PARAM_ENGINE; mCachedParams[Engine.PARAM_POSITION_RATE + 1] = String.valueOf(Engine.DEFAULT_RATE); @@ -381,10 +392,10 @@ public class TextToSpeech { mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = defaultLoc.getISO3Language(); mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = defaultLoc.getISO3Country(); mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = defaultLoc.getVariant(); - mCachedParams[Engine.PARAM_POSITION_STREAM + 1] = String.valueOf(Engine.DEFAULT_STREAM); mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = ""; + mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = Engine.DEFAULT_SYNTH; initTts(); } @@ -684,6 +695,10 @@ public class TextToSpeech { if (extra != null) { mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = extra; } + extra = params.get(Engine.KEY_PARAM_ENGINE); + if (extra != null) { + mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = extra; + } } result = mITts.speak(mPackageName, text, queueMode, mCachedParams); } catch (RemoteException e) { @@ -819,7 +834,7 @@ public class TextToSpeech { mStarted = false; initTts(); } finally { - return result; + return result; } } } @@ -894,7 +909,7 @@ public class TextToSpeech { mStarted = false; initTts(); } finally { - return result; + return result; } } } @@ -943,7 +958,7 @@ public class TextToSpeech { mStarted = false; initTts(); } finally { - return result; + return result; } } } @@ -990,7 +1005,7 @@ public class TextToSpeech { mStarted = false; initTts(); } finally { - return result; + return result; } } } @@ -1046,7 +1061,7 @@ public class TextToSpeech { mStarted = false; initTts(); } finally { - return result; + return result; } } } @@ -1064,7 +1079,7 @@ public class TextToSpeech { return null; } try { - String[] locStrings = mITts.getLanguage(); + String[] locStrings = mITts.getLanguage(); if ((locStrings != null) && (locStrings.length == 3)) { return new Locale(locStrings[0], locStrings[1], locStrings[2]); } else { @@ -1131,7 +1146,7 @@ public class TextToSpeech { mStarted = false; initTts(); } finally { - return result; + return result; } } } @@ -1166,6 +1181,10 @@ public class TextToSpeech { if (extra != null) { mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = extra; } + extra = params.get(Engine.KEY_PARAM_ENGINE); + if (extra != null) { + mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = extra; + } } if (mITts.synthesizeToFile(mPackageName, text, mCachedParams, filename)){ result = SUCCESS; @@ -1214,19 +1233,19 @@ public class TextToSpeech { * * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. */ - public int setOnUtteranceCompletedListener(
- final OnUtteranceCompletedListener listener) {
+ public int setOnUtteranceCompletedListener( + final OnUtteranceCompletedListener listener) { synchronized (mStartLock) { int result = ERROR; if (!mStarted) { return result; } mITtscallback = new ITtsCallback.Stub() { - public void utteranceCompleted(String utteranceId) throws RemoteException {
- if (listener != null) {
- listener.onUtteranceCompleted(utteranceId);
- }
- }
+ public void utteranceCompleted(String utteranceId) throws RemoteException { + if (listener != null) { + listener.onUtteranceCompleted(utteranceId); + } + } }; try { result = mITts.registerCallback(mPackageName, mITtscallback); @@ -1251,7 +1270,50 @@ public class TextToSpeech { } finally { return result; } - }
+ } + } + + /** + * Sets the speech synthesis engine to be used by its packagename. + * + * @param enginePackageName + * The packagename for the synthesis engine (ie, "com.svox.pico") + * + * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}. + */ + public int setEngineByPackageName(String enginePackageName) { + synchronized (mStartLock) { + int result = TextToSpeech.ERROR; + if (!mStarted) { + return result; + } + try { + result = mITts.setEngineByPackageName(enginePackageName); + if (result == TextToSpeech.SUCCESS){ + mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = enginePackageName; + } + } catch (RemoteException e) { + // TTS died; restart it. + Log.e("TextToSpeech.java - setEngineByPackageName", "RemoteException"); + e.printStackTrace(); + mStarted = false; + initTts(); + } catch (NullPointerException e) { + // TTS died; restart it. + Log.e("TextToSpeech.java - setEngineByPackageName", "NullPointerException"); + e.printStackTrace(); + mStarted = false; + initTts(); + } catch (IllegalStateException e) { + // TTS died; restart it. + Log.e("TextToSpeech.java - setEngineByPackageName", "IllegalStateException"); + e.printStackTrace(); + mStarted = false; + initTts(); + } finally { + return result; + } + } } } diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java index 0d04b13..7b307f8 100644 --- a/core/java/android/text/method/ArrowKeyMovementMethod.java +++ b/core/java/android/text/method/ArrowKeyMovementMethod.java @@ -247,10 +247,11 @@ implements MovementMethod KeyEvent.META_SHIFT_ON) == 1) || (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0); + int x = (int) event.getX(); + int y = (int) event.getY(); + int offset = getOffset(x, y, widget); + if (cap) { - int x = (int) event.getX(); - int y = (int) event.getY(); - int offset = getOffset(x, y, widget); buffer.setSpan(LAST_TAP_DOWN, offset, offset, Spannable.SPAN_POINT_POINT); @@ -260,6 +261,30 @@ implements MovementMethod // without this, users would get booted out of select // mode once the view detected it needed to scroll. widget.getParent().requestDisallowInterceptTouchEvent(true); + } else { + OnePointFiveTapState[] tap = buffer.getSpans(0, buffer.length(), + OnePointFiveTapState.class); + + if (tap.length > 0) { + if (event.getEventTime() - tap[0].mWhen <= + ViewConfiguration.getDoubleTapTimeout() && + sameWord(buffer, offset, Selection.getSelectionEnd(buffer))) { + + tap[0].active = true; + MetaKeyKeyListener.startSelecting(widget, buffer); + widget.getParent().requestDisallowInterceptTouchEvent(true); + buffer.setSpan(LAST_TAP_DOWN, offset, offset, + Spannable.SPAN_POINT_POINT); + } + + tap[0].mWhen = event.getEventTime(); + } else { + OnePointFiveTapState newtap = new OnePointFiveTapState(); + newtap.mWhen = event.getEventTime(); + newtap.active = false; + buffer.setSpan(newtap, 0, buffer.length(), + Spannable.SPAN_INCLUSIVE_INCLUSIVE); + } } } else if (event.getAction() == MotionEvent.ACTION_MOVE ) { boolean cap = (MetaKeyKeyListener.getMetaState(buffer, @@ -287,7 +312,7 @@ implements MovementMethod // user started the selection) int lastDownOffset = buffer.getSpanStart(LAST_TAP_DOWN); - // Compute the selection boundries + // Compute the selection boundaries int spanstart; int spanend; if (offset >= lastDownOffset) { @@ -324,6 +349,19 @@ implements MovementMethod // XXX should do the same adjust for x as we do for the line. + OnePointFiveTapState[] onepointfivetap = buffer.getSpans(0, buffer.length(), + OnePointFiveTapState.class); + if (onepointfivetap.length > 0 && onepointfivetap[0].active && + Selection.getSelectionStart(buffer) == Selection.getSelectionEnd(buffer)) { + // If we've set select mode, because there was a onepointfivetap, + // but there was no ensuing swipe gesture, undo the select mode + // and remove reference to the last onepointfivetap. + MetaKeyKeyListener.stopSelecting(widget, buffer); + for (int i=0; i < onepointfivetap.length; i++) { + buffer.removeSpan(onepointfivetap[i]); + } + buffer.removeSpan(LAST_TAP_DOWN); + } boolean cap = (MetaKeyKeyListener.getMetaState(buffer, KeyEvent.META_SHIFT_ON) == 1) || (MetaKeyKeyListener.getMetaState(buffer, @@ -335,10 +373,10 @@ implements MovementMethod if (tap.length > 0) { if (event.getEventTime() - tap[0].mWhen <= - ViewConfiguration.getDoubleTapTimeout()) { - if (sameWord(buffer, off, Selection.getSelectionEnd(buffer))) { - doubletap = true; - } + ViewConfiguration.getDoubleTapTimeout() && + sameWord(buffer, off, Selection.getSelectionEnd(buffer))) { + + doubletap = true; } tap[0].mWhen = event.getEventTime(); @@ -351,6 +389,11 @@ implements MovementMethod if (cap) { buffer.removeSpan(LAST_TAP_DOWN); + if (onepointfivetap.length > 0 && onepointfivetap[0].active) { + // If we selecting something with the onepointfivetap-and + // swipe gesture, stop it on finger up. + MetaKeyKeyListener.stopSelecting(widget, buffer); + } } else if (doubletap) { Selection.setSelection(buffer, findWordStart(buffer, off), @@ -373,6 +416,19 @@ implements MovementMethod long mWhen; } + /* We check for a onepointfive tap. This is similar to + * doubletap gesture (where a finger goes down, up, down, up, in a short + * time period), except in the onepointfive tap, a users finger only needs + * to go down, up, down in a short time period. We detect this type of tap + * to implement the onepointfivetap-and-swipe selection gesture. + * This gesture allows users to select a segment of text without going + * through the "select text" option in the context menu. + */ + private static class OnePointFiveTapState implements NoCopySpan { + long mWhen; + boolean active; + } + private static boolean sameWord(CharSequence text, int one, int two) { int start = findWordStart(text, one); int end = findWordEnd(text, one); diff --git a/core/java/android/util/EventLog.java b/core/java/android/util/EventLog.java index 81dd96e..b596d32 100644 --- a/core/java/android/util/EventLog.java +++ b/core/java/android/util/EventLog.java @@ -16,134 +16,41 @@ package android.util; -import com.google.android.collect.Lists; - +import java.io.BufferedReader; +import java.io.FileReader; import java.io.IOException; import java.io.UnsupportedEncodingException; +import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; +import java.util.HashMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** - * {@hide} - * Dynamically defined (in terms of event types), space efficient (i.e. "tight") event logging - * to help instrument code for large scale stability and performance monitoring. - * - * Note that this class contains all static methods. This is done for efficiency reasons. - * - * Events for the event log are self-describing binary data structures. They start with a 20 byte - * header (generated automatically) which contains all of the following in order: - * - * <ul> - * <li> Payload length: 2 bytes - length of the non-header portion </li> - * <li> Padding: 2 bytes - no meaning at this time </li> - * <li> Timestamp: - * <ul> - * <li> Seconds: 4 bytes - seconds since Epoch </li> - * <li> Nanoseconds: 4 bytes - plus extra nanoseconds </li> - * </ul></li> - * <li> Process ID: 4 bytes - matching {@link android.os.Process#myPid} </li> - * <li> Thread ID: 4 bytes - matching {@link android.os.Process#myTid} </li> - * </li> - * </ul> + * Access to the system diagnostic event record. System diagnostic events are + * used to record certain system-level events (such as garbage collection, + * activity manager state, system watchdogs, and other low level activity), + * which may be automatically collected and analyzed during system development. * - * The above is followed by a payload, comprised of the following: - * <ul> - * <li> Tag: 4 bytes - unique integer used to identify a particular event. This number is also - * used as a key to map to a string that can be displayed by log reading tools. - * </li> - * <li> Type: 1 byte - can be either {@link #INT}, {@link #LONG}, {@link #STRING}, - * or {@link #LIST}. </li> - * <li> Event log value: the size and format of which is one of: - * <ul> - * <li> INT: 4 bytes </li> - * <li> LONG: 8 bytes </li> - * <li> STRING: - * <ul> - * <li> Size of STRING: 4 bytes </li> - * <li> The string: n bytes as specified in the size fields above. </li> - * </ul></li> - * <li> {@link List LIST}: - * <ul> - * <li> Num items: 1 byte </li> - * <li> N value payloads, where N is the number of items specified above. </li> - * </ul></li> - * </ul> - * </li> - * <li> '\n': 1 byte - an automatically generated newline, used to help detect and recover from log - * corruption and enable standard unix tools like grep, tail and wc to operate - * on event logs. </li> - * </ul> + * <p>This is <b>not</b> the main "logcat" debugging log ({@link android.util.Log})! + * These diagnostic events are for system integrators, not application authors. * - * Note that all output is done in the endian-ness of the device (as determined - * by {@link ByteOrder#nativeOrder()}). + * <p>Events use integer tag codes corresponding to /system/etc/event-log-tags. + * They carry a payload of one or more int, long, or String values. The + * event-log-tags file defines the payload contents for each type code. */ - public class EventLog { + private static final String TAG = "EventLog"; - // Value types - public static final byte INT = 0; - public static final byte LONG = 1; - public static final byte STRING = 2; - public static final byte LIST = 3; + private static final String TAGS_FILE = "/system/etc/event-log-tags"; + private static final String COMMENT_PATTERN = "^\\s*(#.*)?$"; + private static final String TAG_PATTERN = "^\\s*(\\d+)\\s+(\\w+)\\s*(\\(.*\\))?\\s*$"; + private static HashMap<String, Integer> sTagCodes = null; + private static HashMap<Integer, String> sTagNames = null; - /** - * An immutable tuple used to log a heterogeneous set of loggable items. - * The items can be Integer, Long, String, or {@link List}. - * The maximum number of items is 127 - */ - public static final class List { - private Object[] mItems; - - /** - * Get a particular tuple item - * @param pos The position of the item in the tuple - */ - public final Object getItem(int pos) { - return mItems[pos]; - } - - /** - * Get the number of items in the tuple. - */ - public final byte getNumItems() { - return (byte) mItems.length; - } - - /** - * Create a new tuple. - * @param items The items to create the tuple with, as varargs. - * @throws IllegalArgumentException if the arguments are too few (0), - * too many, or aren't loggable types. - */ - public List(Object... items) throws IllegalArgumentException { - if (items.length > Byte.MAX_VALUE) { - throw new IllegalArgumentException( - "A List must have fewer than " - + Byte.MAX_VALUE + " items in it."); - } - for (int i = 0; i < items.length; i++) { - final Object item = items[i]; - if (item == null) { - // Would be nice to be able to write null strings... - items[i] = ""; - } else if (!(item instanceof List || - item instanceof String || - item instanceof Integer || - item instanceof Long)) { - throw new IllegalArgumentException( - "Attempt to create a List with illegal item type."); - } - } - this.mItems = items; - } - } - - /** - * A previously logged event read from the logs. - */ + /** A previously logged event read from the logs. */ public static final class Event { private final ByteBuffer mBuffer; @@ -158,77 +65,84 @@ public class EventLog { private static final int TAG_OFFSET = 20; private static final int DATA_START = 24; + // Value types + private static final byte INT_TYPE = 0; + private static final byte LONG_TYPE = 1; + private static final byte STRING_TYPE = 2; + private static final byte LIST_TYPE = 3; + /** @param data containing event, read from the system */ - public Event(byte[] data) { + /*package*/ Event(byte[] data) { mBuffer = ByteBuffer.wrap(data); mBuffer.order(ByteOrder.nativeOrder()); } + /** @return the process ID which wrote the log entry */ public int getProcessId() { return mBuffer.getInt(PROCESS_OFFSET); } + /** @return the thread ID which wrote the log entry */ public int getThreadId() { return mBuffer.getInt(THREAD_OFFSET); } + /** @return the wall clock time when the entry was written */ public long getTimeNanos() { return mBuffer.getInt(SECONDS_OFFSET) * 1000000000l + mBuffer.getInt(NANOSECONDS_OFFSET); } + /** @return the type tag code of the entry */ public int getTag() { return mBuffer.getInt(TAG_OFFSET); } - /** @return one of Integer, Long, String, or List. */ + /** @return one of Integer, Long, String, null, or Object[] of same. */ public synchronized Object getData() { - mBuffer.limit(PAYLOAD_START + mBuffer.getShort(LENGTH_OFFSET)); - mBuffer.position(DATA_START); // Just after the tag. - return decodeObject(); - } - - public byte[] getRawData() { - return mBuffer.array(); + try { + mBuffer.limit(PAYLOAD_START + mBuffer.getShort(LENGTH_OFFSET)); + mBuffer.position(DATA_START); // Just after the tag. + return decodeObject(); + } catch (IllegalArgumentException e) { + Log.wtf(TAG, "Illegal entry payload: tag=" + getTag(), e); + return null; + } catch (BufferUnderflowException e) { + Log.wtf(TAG, "Truncated entry payload: tag=" + getTag(), e); + return null; + } } /** @return the loggable item at the current position in mBuffer. */ private Object decodeObject() { - if (mBuffer.remaining() < 1) return null; - switch (mBuffer.get()) { - case INT: - if (mBuffer.remaining() < 4) return null; + byte type = mBuffer.get(); + switch (type) { + case INT_TYPE: return (Integer) mBuffer.getInt(); - case LONG: - if (mBuffer.remaining() < 8) return null; + case LONG_TYPE: return (Long) mBuffer.getLong(); - case STRING: + case STRING_TYPE: try { - if (mBuffer.remaining() < 4) return null; int length = mBuffer.getInt(); - if (length < 0 || mBuffer.remaining() < length) return null; int start = mBuffer.position(); mBuffer.position(start + length); return new String(mBuffer.array(), start, length, "UTF-8"); } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); // UTF-8 is guaranteed. + Log.wtf(TAG, "UTF-8 is not supported", e); + return null; } - case LIST: - if (mBuffer.remaining() < 1) return null; + case LIST_TYPE: int length = mBuffer.get(); - if (length < 0) return null; + if (length < 0) length += 256; // treat as signed byte Object[] array = new Object[length]; - for (int i = 0; i < length; ++i) { - array[i] = decodeObject(); - if (array[i] == null) return null; - } - return new List(array); + for (int i = 0; i < length; ++i) array[i] = decodeObject(); + return array; default: - return null; + throw new IllegalArgumentException("Unknown entry type: " + type); } } } @@ -236,46 +150,36 @@ public class EventLog { // We assume that the native methods deal with any concurrency issues. /** - * Send an event log message. - * @param tag An event identifer + * Record an event log message. + * @param tag The event type tag code * @param value A value to log * @return The number of bytes written */ public static native int writeEvent(int tag, int value); /** - * Send an event log message. - * @param tag An event identifer + * Record an event log message. + * @param tag The event type tag code * @param value A value to log * @return The number of bytes written */ public static native int writeEvent(int tag, long value); /** - * Send an event log message. - * @param tag An event identifer + * Record an event log message. + * @param tag The event type tag code * @param str A value to log * @return The number of bytes written */ public static native int writeEvent(int tag, String str); /** - * Send an event log message. - * @param tag An event identifer - * @param list A {@link List} to log - * @return The number of bytes written - */ - public static native int writeEvent(int tag, List list); - - /** - * Send an event log message. - * @param tag An event identifer + * Record an event log message. + * @param tag The event type tag code * @param list A list of values to log * @return The number of bytes written */ - public static int writeEvent(int tag, Object... list) { - return writeEvent(tag, new List(list)); - } + public static native int writeEvent(int tag, Object... list); /** * Read events from the log, filtered by type. @@ -287,11 +191,65 @@ public class EventLog { throws IOException; /** - * Read events from a file. - * @param path to read from - * @param output container to add events into - * @throws IOException if something goes wrong reading events + * Get the name associated with an event type tag code. + * @param tag code to look up + * @return the name of the tag, or null if no tag has that number */ - public static native void readEvents(String path, Collection<Event> output) - throws IOException; + public static String getTagName(int tag) { + readTagsFile(); + return sTagNames.get(tag); + } + + /** + * Get the event type tag code associated with an event name. + * @param name of event to look up + * @return the tag code, or -1 if no tag has that name + */ + public static int getTagCode(String name) { + readTagsFile(); + Integer code = sTagCodes.get(name); + return code != null ? code : -1; + } + + /** + * Read TAGS_FILE, populating sTagCodes and sTagNames, if not already done. + */ + private static synchronized void readTagsFile() { + if (sTagCodes != null && sTagNames != null) return; + + sTagCodes = new HashMap<String, Integer>(); + sTagNames = new HashMap<Integer, String>(); + + Pattern comment = Pattern.compile(COMMENT_PATTERN); + Pattern tag = Pattern.compile(TAG_PATTERN); + BufferedReader reader = null; + String line; + + try { + reader = new BufferedReader(new FileReader(TAGS_FILE), 256); + while ((line = reader.readLine()) != null) { + if (comment.matcher(line).matches()) continue; + + Matcher m = tag.matcher(line); + if (!m.matches()) { + Log.wtf(TAG, "Bad entry in " + TAGS_FILE + ": " + line); + continue; + } + + try { + int num = Integer.parseInt(m.group(1)); + String name = m.group(2); + sTagCodes.put(name, num); + sTagNames.put(num, name); + } catch (NumberFormatException e) { + Log.wtf(TAG, "Error in " + TAGS_FILE + ": " + line, e); + } + } + } catch (IOException e) { + Log.wtf(TAG, "Error reading " + TAGS_FILE, e); + // Leave the maps existing but unpopulated + } finally { + try { if (reader != null) reader.close(); } catch (IOException e) {} + } + } } diff --git a/core/java/android/util/EventLogTags.java b/core/java/android/util/EventLogTags.java index be905e3..5cf5332 100644 --- a/core/java/android/util/EventLogTags.java +++ b/core/java/android/util/EventLogTags.java @@ -25,16 +25,14 @@ import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; -/** Parsed representation of /etc/event-log-tags. */ +/** + * @deprecated This class is no longer functional. + * Use {@link android.util.EventLog} instead. + */ public class EventLogTags { - private final static String TAG = "EventLogTags"; - - private final static String TAGS_FILE = "/etc/event-log-tags"; - public static class Description { public final int mTag; public final String mName; - // TODO: Parse parameter descriptions when anyone has a use for them. Description(int tag, String name) { mTag = tag; @@ -42,49 +40,11 @@ public class EventLogTags { } } - private final static Pattern COMMENT_PATTERN = Pattern.compile( - "^\\s*(#.*)?$"); - - private final static Pattern TAG_PATTERN = Pattern.compile( - "^\\s*(\\d+)\\s+(\\w+)\\s*(\\(.*\\))?\\s*$"); + public EventLogTags() throws IOException {} - private final HashMap<String, Description> mNameMap = - new HashMap<String, Description>(); - - private final HashMap<Integer, Description> mTagMap = - new HashMap<Integer, Description>(); - - public EventLogTags() throws IOException { - this(new BufferedReader(new FileReader(TAGS_FILE), 256)); - } + public EventLogTags(BufferedReader input) throws IOException {} - public EventLogTags(BufferedReader input) throws IOException { - String line; - while ((line = input.readLine()) != null) { - Matcher m = COMMENT_PATTERN.matcher(line); - if (m.matches()) continue; + public Description get(String name) { return null; } - m = TAG_PATTERN.matcher(line); - if (m.matches()) { - try { - int tag = Integer.parseInt(m.group(1)); - Description d = new Description(tag, m.group(2)); - mNameMap.put(d.mName, d); - mTagMap.put(d.mTag, d); - } catch (NumberFormatException e) { - Log.e(TAG, "Error in event log tags entry: " + line, e); - } - } else { - Log.e(TAG, "Can't parse event log tags entry: " + line); - } - } - } - - public Description get(String name) { - return mNameMap.get(name); - } - - public Description get(int tag) { - return mTagMap.get(tag); - } + public Description get(int tag) { return null; } } diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java index e95d0be..75b1b90 100644 --- a/core/java/android/util/Log.java +++ b/core/java/android/util/Log.java @@ -81,6 +81,13 @@ public final class Log { */ public static final int ASSERT = 7; + /** + * Exception class used to capture a stack trace in {@link #wtf()}. + */ + private static class TerribleFailure extends Exception { + TerribleFailure(String msg, Throwable cause) { super(msg, cause); } + } + private Log() { } @@ -170,24 +177,24 @@ public final class Log { /** * Checks to see whether or not a log for the specified tag is loggable at the specified level. - * + * * The default level of any tag is set to INFO. This means that any level above and including * INFO will be logged. Before you make any calls to a logging method you should check to see * if your tag should be logged. You can change the default level by setting a system property: * 'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>' - * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPRESS will + * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will * turn off all logging for your tag. You can also create a local.prop file that with the * following in it: * 'log.tag.<YOUR_LOG_TAG>=<LEVEL>' * and place that in /data/local.prop. - * + * * @param tag The tag to check. * @param level The level to check. * @return Whether or not that this is allowed to be logged. * @throws IllegalArgumentException is thrown if the tag.length() > 23. */ public static native boolean isLoggable(String tag, int level); - + /* * Send a {@link #WARN} log message and log the exception. * @param tag Used to identify the source of a log message. It usually identifies @@ -220,6 +227,43 @@ public final class Log { } /** + * What a Terrible Failure: Report a condition that should never happen. + * The error will always be logged at level ASSERT with the call stack. + * Depending on system configuration, a report may be added to the + * {@link android.os.DropBoxManager} and/or the process may be terminated + * immediately with an error dialog. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + */ + public static int wtf(String tag, String msg) { + return wtf(tag, msg, null); + } + + /** + * What a Terrible Failure: Report an exception that should never happen. + * Similar to {@link #wtf(String, String)}, with an exception to log. + * @param tag Used to identify the source of a log message. + * @param tr An exception to log. + */ + public static int wtf(String tag, Throwable tr) { + return wtf(tag, tr.getMessage(), tr); + } + + /** + * What a Terrible Failure: Report an exception that should never happen. + * Similar to {@link #wtf(String, Throwable)}, with a message as well. + * @param tag Used to identify the source of a log message. + * @param msg The message you would like logged. + * @param tr An exception to log. May be null. + */ + public static int wtf(String tag, String msg, Throwable tr) { + tr = new TerribleFailure(msg, tr); + int bytes = println(ASSERT, tag, getStackTraceString(tr)); + RuntimeInit.wtf(tag, tr); + return bytes; + } + + /** * Handy function to get a loggable stack trace from a Throwable * @param tr An exception to log */ diff --git a/core/java/android/view/TransformGestureDetector.java b/core/java/android/view/TransformGestureDetector.java new file mode 100644 index 0000000..196716a --- /dev/null +++ b/core/java/android/view/TransformGestureDetector.java @@ -0,0 +1,316 @@ +/* + * 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 android.view; + +import android.content.Context; +import android.util.Log; +import android.view.GestureDetector.SimpleOnGestureListener; + +/** + * Detects transformation gestures involving more than one pointer ("multitouch") + * using the supplied {@link MotionEvent}s. The {@link OnGestureListener} callback + * will notify users when a particular gesture event has occurred. This class + * should only be used with {@link MotionEvent}s reported via touch. + * + * To use this class: + * <ul> + * <li>Create an instance of the {@code TransformGestureDetector} for your + * {@link View} + * <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call + * {@link #onTouchEvent(MotionEvent)}. The methods defined in your + * callback will be executed when the events occur. + * </ul> + * @hide Pending API approval + */ +public class TransformGestureDetector { + /** + * The listener for receiving notifications when gestures occur. + * If you want to listen for all the different gestures then implement + * this interface. If you only want to listen for a subset it might + * be easier to extend {@link SimpleOnGestureListener}. + * + * An application will receive events in the following order: + * One onTransformBegin() + * Zero or more onTransform() + * One onTransformEnd() or onTransformFling() + */ + public interface OnTransformGestureListener { + /** + * Responds to transformation events for a gesture in progress. + * Reported by pointer motion. + * + * @param detector The detector reporting the event - use this to + * retrieve extended info about event state. + * @return true if the event was handled, false otherwise. + */ + public boolean onTransform(TransformGestureDetector detector); + + /** + * Responds to the beginning of a transformation gesture. Reported by + * new pointers going down. + * + * @param detector The detector reporting the event - use this to + * retrieve extended info about event state. + * @return true if the event was handled, false otherwise. + */ + public boolean onTransformBegin(TransformGestureDetector detector); + + /** + * Responds to the end of a transformation gesture. Reported by existing + * pointers going up. If the end of a gesture would result in a fling, + * onTransformFling is called instead. + * + * @param detector The detector reporting the event - use this to + * retrieve extended info about event state. + * @return true if the event was handled, false otherwise. + */ + public boolean onTransformEnd(TransformGestureDetector detector); + + /** + * Responds to the end of a transformation gesture that begins a fling. + * Reported by existing pointers going up. If the end of a gesture + * would not result in a fling, onTransformEnd is called instead. + * + * @param detector The detector reporting the event - use this to + * retrieve extended info about event state. + * @return true if the event was handled, false otherwise. + */ + public boolean onTransformFling(TransformGestureDetector detector); + } + + private static final boolean DEBUG = false; + + private static final int INITIAL_EVENT_IGNORES = 2; + + private Context mContext; + private float mTouchSizeScale; + private OnTransformGestureListener mListener; + private int mVelocityTimeUnits; + private MotionEvent mInitialEvent; + + private MotionEvent mPrevEvent; + private MotionEvent mCurrEvent; + private VelocityTracker mVelocityTracker; + + private float mCenterX; + private float mCenterY; + private float mTransX; + private float mTransY; + private float mPrevFingerDiffX; + private float mPrevFingerDiffY; + private float mCurrFingerDiffX; + private float mCurrFingerDiffY; + private float mRotateDegrees; + private float mCurrLen; + private float mPrevLen; + private float mScaleFactor; + + // Units in pixels. Current value is pulled out of thin air for debugging only. + private float mPointerJumpLimit = 30; + + private int mEventIgnoreCount; + + public TransformGestureDetector(Context context, OnTransformGestureListener listener, + int velocityTimeUnits) { + mContext = context; + mListener = listener; + mTouchSizeScale = context.getResources().getDisplayMetrics().widthPixels/3; + mVelocityTimeUnits = velocityTimeUnits; + mEventIgnoreCount = INITIAL_EVENT_IGNORES; + } + + public TransformGestureDetector(Context context, OnTransformGestureListener listener) { + this(context, listener, 1000); + } + + public boolean onTouchEvent(MotionEvent event) { + final int action = event.getAction(); + boolean handled = true; + + if (mInitialEvent == null) { + // No transform gesture in progress + if ((action == MotionEvent.ACTION_POINTER_1_DOWN || + action == MotionEvent.ACTION_POINTER_2_DOWN) && + event.getPointerCount() >= 2) { + // We have a new multi-finger gesture + mInitialEvent = MotionEvent.obtain(event); + mPrevEvent = MotionEvent.obtain(event); + mVelocityTracker = VelocityTracker.obtain(); + handled = mListener.onTransformBegin(this); + } + } else { + // Transform gesture in progress - attempt to handle it + switch (action) { + case MotionEvent.ACTION_POINTER_1_UP: + case MotionEvent.ACTION_POINTER_2_UP: + // Gesture ended + handled = mListener.onTransformEnd(this); + + reset(); + break; + + case MotionEvent.ACTION_CANCEL: + handled = mListener.onTransformEnd(this); + + reset(); + break; + + case MotionEvent.ACTION_MOVE: + setContext(event); + + // Our first few events can be crazy from some touchscreens - drop them. + if (mEventIgnoreCount == 0) { + mVelocityTracker.addMovement(event); + handled = mListener.onTransform(this); + } else { + mEventIgnoreCount--; + } + + mPrevEvent.recycle(); + mPrevEvent = MotionEvent.obtain(event); + break; + } + } + return handled; + } + + private void setContext(MotionEvent curr) { + mCurrEvent = MotionEvent.obtain(curr); + + mRotateDegrees = -1; + mCurrLen = -1; + mPrevLen = -1; + mScaleFactor = -1; + + final MotionEvent prev = mPrevEvent; + + float px0 = prev.getX(0); + float py0 = prev.getY(0); + float px1 = prev.getX(1); + float py1 = prev.getY(1); + float cx0 = curr.getX(0); + float cy0 = curr.getY(0); + float cx1 = curr.getX(1); + float cy1 = curr.getY(1); + + // Some touchscreens do weird things with pointer values where points are + // too close along one axis. Try to detect this here and smooth things out. + // The main indicator is that we get the X or Y value from the other pointer. + final float dx0 = cx0 - px0; + final float dy0 = cy0 - py0; + final float dx1 = cx1 - px1; + final float dy1 = cy1 - py1; + + if (cx0 == cx1) { + if (Math.abs(dx0) > mPointerJumpLimit) { + cx0 = px0; + } else if (Math.abs(dx1) > mPointerJumpLimit) { + cx1 = px1; + } + } else if (cy0 == cy1) { + if (Math.abs(dy0) > mPointerJumpLimit) { + cy0 = py0; + } else if (Math.abs(dy1) > mPointerJumpLimit) { + cy1 = py1; + } + } + + final float pvx = px1 - px0; + final float pvy = py1 - py0; + final float cvx = cx1 - cx0; + final float cvy = cy1 - cy0; + mPrevFingerDiffX = pvx; + mPrevFingerDiffY = pvy; + mCurrFingerDiffX = cvx; + mCurrFingerDiffY = cvy; + + final float pmidx = px0 + pvx * 0.5f; + final float pmidy = py0 + pvy * 0.5f; + final float cmidx = cx0 + cvx * 0.5f; + final float cmidy = cy0 + cvy * 0.5f; + + mCenterX = cmidx; + mCenterY = cmidy; + mTransX = cmidx - pmidx; + mTransY = cmidy - pmidy; + } + + private void reset() { + if (mInitialEvent != null) { + mInitialEvent.recycle(); + mInitialEvent = null; + } + if (mPrevEvent != null) { + mPrevEvent.recycle(); + mPrevEvent = null; + } + if (mCurrEvent != null) { + mCurrEvent.recycle(); + mCurrEvent = null; + } + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + mEventIgnoreCount = INITIAL_EVENT_IGNORES; + } + + public float getCenterX() { + return mCenterX; + } + + public float getCenterY() { + return mCenterY; + } + + public float getTranslateX() { + return mTransX; + } + + public float getTranslateY() { + return mTransY; + } + + public float getCurrentSpan() { + if (mCurrLen == -1) { + final float cvx = mCurrFingerDiffX; + final float cvy = mCurrFingerDiffY; + mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy); + } + return mCurrLen; + } + + public float getPreviousSpan() { + if (mPrevLen == -1) { + final float pvx = mPrevFingerDiffX; + final float pvy = mPrevFingerDiffY; + mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy); + } + return mPrevLen; + } + + public float getScaleFactor() { + if (mScaleFactor == -1) { + mScaleFactor = getCurrentSpan() / getPreviousSpan(); + } + return mScaleFactor; + } + + public float getRotation() { + throw new UnsupportedOperationException(); + } +} diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index 5d89c46..9581080 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -55,12 +55,12 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { } }, 2)); - final float mPastX[] = new float[NUM_PAST]; - final float mPastY[] = new float[NUM_PAST]; - final long mPastTime[] = new long[NUM_PAST]; + final float mPastX[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; + final float mPastY[][] = new float[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; + final long mPastTime[][] = new long[MotionEvent.BASE_AVAIL_POINTERS][NUM_PAST]; - float mYVelocity; - float mXVelocity; + float mYVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS]; + float mXVelocity[] = new float[MotionEvent.BASE_AVAIL_POINTERS]; private VelocityTracker mNext; @@ -105,7 +105,9 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * Reset the velocity tracker back to its initial state. */ public void clear() { - mPastTime[0] = 0; + for (int i = 0; i < MotionEvent.BASE_AVAIL_POINTERS; i++) { + mPastTime[i][0] = 0; + } } /** @@ -120,18 +122,21 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { public void addMovement(MotionEvent ev) { long time = ev.getEventTime(); final int N = ev.getHistorySize(); - for (int i=0; i<N; i++) { - addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i), - ev.getHistoricalEventTime(i)); + final int pointerCount = ev.getPointerCount(); + for (int p = 0; p < pointerCount; p++) { + for (int i=0; i<N; i++) { + addPoint(p, ev.getHistoricalX(p, i), ev.getHistoricalY(p, i), + ev.getHistoricalEventTime(i)); + } + addPoint(p, ev.getX(p), ev.getY(p), time); } - addPoint(ev.getX(), ev.getY(), time); } - private void addPoint(float x, float y, long time) { + private void addPoint(int pos, float x, float y, long time) { int drop = -1; int i; if (localLOGV) Log.v(TAG, "Adding past y=" + y + " time=" + time); - final long[] pastTime = mPastTime; + final long[] pastTime = mPastTime[pos]; for (i=0; i<NUM_PAST; i++) { if (pastTime[i] == 0) { break; @@ -146,8 +151,8 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { drop = 0; } if (drop == i) drop--; - final float[] pastX = mPastX; - final float[] pastY = mPastY; + final float[] pastX = mPastX[pos]; + final float[] pastY = mPastY[pos]; if (drop >= 0) { if (localLOGV) Log.v(TAG, "Dropping up to #" + drop); final int start = drop+1; @@ -190,44 +195,48 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * must be positive. */ public void computeCurrentVelocity(int units, float maxVelocity) { - final float[] pastX = mPastX; - final float[] pastY = mPastY; - final long[] pastTime = mPastTime; - - // Kind-of stupid. - final float oldestX = pastX[0]; - final float oldestY = pastY[0]; - final long oldestTime = pastTime[0]; - float accumX = 0; - float accumY = 0; - int N=0; - while (N < NUM_PAST) { - if (pastTime[N] == 0) { - break; + for (int pos = 0; pos < MotionEvent.BASE_AVAIL_POINTERS; pos++) { + final float[] pastX = mPastX[pos]; + final float[] pastY = mPastY[pos]; + final long[] pastTime = mPastTime[pos]; + + // Kind-of stupid. + final float oldestX = pastX[0]; + final float oldestY = pastY[0]; + final long oldestTime = pastTime[0]; + float accumX = 0; + float accumY = 0; + int N=0; + while (N < NUM_PAST) { + if (pastTime[N] == 0) { + break; + } + N++; } - N++; - } - // Skip the last received event, since it is probably pretty noisy. - if (N > 3) N--; - - for (int i=1; i < N; i++) { - final int dur = (int)(pastTime[i] - oldestTime); - if (dur == 0) continue; - float dist = pastX[i] - oldestX; - float vel = (dist/dur) * units; // pixels/frame. - if (accumX == 0) accumX = vel; - else accumX = (accumX + vel) * .5f; - - dist = pastY[i] - oldestY; - vel = (dist/dur) * units; // pixels/frame. - if (accumY == 0) accumY = vel; - else accumY = (accumY + vel) * .5f; + // Skip the last received event, since it is probably pretty noisy. + if (N > 3) N--; + + for (int i=1; i < N; i++) { + final int dur = (int)(pastTime[i] - oldestTime); + if (dur == 0) continue; + float dist = pastX[i] - oldestX; + float vel = (dist/dur) * units; // pixels/frame. + if (accumX == 0) accumX = vel; + else accumX = (accumX + vel) * .5f; + + dist = pastY[i] - oldestY; + vel = (dist/dur) * units; // pixels/frame. + if (accumY == 0) accumY = vel; + else accumY = (accumY + vel) * .5f; + } + mXVelocity[pos] = accumX < 0.0f ? Math.max(accumX, -maxVelocity) + : Math.min(accumX, maxVelocity); + mYVelocity[pos] = accumY < 0.0f ? Math.max(accumY, -maxVelocity) + : Math.min(accumY, maxVelocity); + + if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity=" + + mXVelocity + " N=" + N); } - mXVelocity = accumX < 0.0f ? Math.max(accumX, -maxVelocity) : Math.min(accumX, maxVelocity); - mYVelocity = accumY < 0.0f ? Math.max(accumY, -maxVelocity) : Math.min(accumY, maxVelocity); - - if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity=" - + mXVelocity + " N=" + N); } /** @@ -237,7 +246,7 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed X velocity. */ public float getXVelocity() { - return mXVelocity; + return mXVelocity[0]; } /** @@ -247,6 +256,32 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * @return The previously computed Y velocity. */ public float getYVelocity() { - return mYVelocity; + return mYVelocity[0]; + } + + /** + * Retrieve the last computed X velocity. You must first call + * {@link #computeCurrentVelocity(int)} before calling this function. + * + * @param pos Which pointer's velocity to return. + * @return The previously computed X velocity. + * + * @hide Pending API approval + */ + public float getXVelocity(int pos) { + return mXVelocity[pos]; + } + + /** + * Retrieve the last computed Y velocity. You must first call + * {@link #computeCurrentVelocity(int)} before calling this function. + * + * @param pos Which pointer's velocity to return. + * @return The previously computed Y velocity. + * + * @hide Pending API approval + */ + public float getYVelocity(int pos) { + return mYVelocity[pos]; } } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 788f04a..df4cab0 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -379,7 +379,7 @@ import java.util.WeakHashMap; * dimension, it can specify one of: * <ul> * <li> an exact number - * <li>FILL_PARENT, which means the view wants to be as big as its parent + * <li>MATCH_PARENT, which means the view wants to be as big as its parent * (minus padding) * <li> WRAP_CONTENT, which means that the view wants to be just big enough to * enclose its content (plus padding). @@ -1494,6 +1494,16 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @hide */ static final int OPAQUE_MASK = 0x01800000; + + /** + * Indicates a prepressed state; + * the short time between ACTION_DOWN and recognizing + * a 'real' press. Prepressed is used to recognize quick taps + * even when they are shorter than ViewConfiguration.getTapTimeout(). + * + * @hide + */ + private static final int PREPRESSED = 0x02000000; /** * The parent this view is attached to. @@ -1722,6 +1732,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility private int mNextFocusDownId = View.NO_ID; private CheckForLongPress mPendingCheckForLongPress; + private CheckForTap mPendingCheckForTap = null; + private UnsetPressedState mUnsetPressedState; /** @@ -1762,6 +1774,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * Special tree observer used when mAttachInfo is null. */ private ViewTreeObserver mFloatingTreeObserver; + + /** + * Cache the touch slop from the context that created the view. + */ + private int mTouchSlop; // Used for debug only static long sInstanceCount = 0; @@ -1777,6 +1794,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mResources = context != null ? context.getResources() : null; mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED; ++sInstanceCount; + mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); } /** @@ -2726,7 +2744,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility setPressed(false); if (!mHasPerformedLongPress) { - cancelLongPress(); + removeLongPressCallback(); } } } @@ -3748,7 +3766,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (imm != null && (mPrivateFlags & FOCUSED) != 0) { imm.focusOut(this); } - cancelLongPress(); + removeLongPressCallback(); onFocusLost(); } else if (imm != null && (mPrivateFlags & FOCUSED) != 0) { imm.focusIn(this); @@ -3951,7 +3969,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility (event.getRepeatCount() == 0)) { setPressed(true); if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { - postCheckForLongClick(); + postCheckForLongClick(0); } return true; } @@ -3994,7 +4012,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (!mHasPerformedLongPress) { // This is a tap, so remove the longpress check - cancelLongPress(); + removeLongPressCallback(); result = performClick(); } @@ -4174,7 +4192,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) { switch (event.getAction()) { case MotionEvent.ACTION_UP: - if ((mPrivateFlags & PRESSED) != 0) { + boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; + if ((mPrivateFlags & PRESSED) != 0 || prepressed) { // take focus if we don't have it already and we should in // touch mode. boolean focusTaken = false; @@ -4184,7 +4203,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility if (!mHasPerformedLongPress) { // This is a tap, so remove the longpress check - cancelLongPress(); + removeLongPressCallback(); // Only perform take click actions if we were in the pressed state if (!focusTaken) { @@ -4196,24 +4215,31 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mUnsetPressedState = new UnsetPressedState(); } - if (!post(mUnsetPressedState)) { + if (prepressed) { + mPrivateFlags |= PRESSED; + refreshDrawableState(); + postDelayed(mUnsetPressedState, + ViewConfiguration.getPressedStateDuration()); + } else if (!post(mUnsetPressedState)) { // If the post failed, unpress right now mUnsetPressedState.run(); } + removeTapCallback(); } break; case MotionEvent.ACTION_DOWN: - mPrivateFlags |= PRESSED; - refreshDrawableState(); - if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { - postCheckForLongClick(); + if (mPendingCheckForTap == null) { + mPendingCheckForTap = new CheckForTap(); } + mPrivateFlags |= PREPRESSED; + postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); break; case MotionEvent.ACTION_CANCEL: mPrivateFlags &= ~PRESSED; refreshDrawableState(); + removeTapCallback(); break; case MotionEvent.ACTION_MOVE: @@ -4221,25 +4247,19 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility final int y = (int) event.getY(); // Be lenient about moving outside of buttons - int slop = ViewConfiguration.get(mContext).getScaledTouchSlop(); + int slop = mTouchSlop; if ((x < 0 - slop) || (x >= getWidth() + slop) || (y < 0 - slop) || (y >= getHeight() + slop)) { // Outside button + removeTapCallback(); if ((mPrivateFlags & PRESSED) != 0) { - // Remove any future long press checks - cancelLongPress(); + // Remove any future long press/tap checks + removeLongPressCallback(); // Need to switch from pressed to not pressed mPrivateFlags &= ~PRESSED; refreshDrawableState(); } - } else { - // Inside button - if ((mPrivateFlags & PRESSED) == 0) { - // Need to switch from not pressed to pressed - mPrivateFlags |= PRESSED; - refreshDrawableState(); - } } break; } @@ -4250,15 +4270,32 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } /** + * Remove the longpress detection timer. + */ + private void removeLongPressCallback() { + if (mPendingCheckForLongPress != null) { + removeCallbacks(mPendingCheckForLongPress); + } + } + + /** + * Remove the tap detection timer. + */ + private void removeTapCallback() { + if (mPendingCheckForTap != null) { + mPrivateFlags &= ~PREPRESSED; + removeCallbacks(mPendingCheckForTap); + } + } + + /** * Cancels a pending long press. Your subclass can use this if you * want the context menu to come up if the user presses and holds * at the same place, but you don't want it to come up if they press * and then move around enough to cause scrolling. */ public void cancelLongPress() { - if (mPendingCheckForLongPress != null) { - removeCallbacks(mPendingCheckForLongPress); - } + removeLongPressCallback(); } /** @@ -5759,7 +5796,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility * @see #onAttachedToWindow() */ protected void onDetachedFromWindow() { - cancelLongPress(); + removeLongPressCallback(); destroyDrawingCache(); } @@ -8420,14 +8457,15 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility } } - private void postCheckForLongClick() { + private void postCheckForLongClick(int delayOffset) { mHasPerformedLongPress = false; if (mPendingCheckForLongPress == null) { mPendingCheckForLongPress = new CheckForLongPress(); } mPendingCheckForLongPress.rememberWindowAttachCount(); - postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout()); + postDelayed(mPendingCheckForLongPress, + ViewConfiguration.getLongPressTimeout() - delayOffset); } private static int[] stateSetUnion(final int[] stateSet1, @@ -8604,6 +8642,17 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility mOriginalWindowAttachCount = mWindowAttachCount; } } + + private final class CheckForTap implements Runnable { + public void run() { + mPrivateFlags &= ~PREPRESSED; + mPrivateFlags |= PRESSED; + refreshDrawableState(); + if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) { + postCheckForLongClick(ViewConfiguration.getTapTimeout()); + } + } + } /** * Interface definition for a callback to be invoked when a key event is diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 993048f..2344c42 100644 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -49,7 +49,7 @@ public class ViewConfiguration { * Defines the duration in milliseconds of the pressed state in child * components. */ - private static final int PRESSED_STATE_DURATION = 85; + private static final int PRESSED_STATE_DURATION = 125; /** * Defines the duration in milliseconds before a press turns into @@ -69,7 +69,7 @@ public class ViewConfiguration { * is a tap or a scroll. If the user does not move within this interval, it is * considered to be a tap. */ - private static final int TAP_TIMEOUT = 100; + private static final int TAP_TIMEOUT = 115; /** * Defines the duration in milliseconds we will wait to see if a touch event diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index 2fd974e..3ebe1c2 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -702,7 +702,7 @@ public class ViewDebug { if (parameter.indexOf('@') != -1) { final String[] ids = parameter.split("@"); final String className = ids[0]; - final int hashCode = Integer.parseInt(ids[1], 16); + final int hashCode = (int) Long.parseLong(ids[1], 16); View view = root.getRootView(); if (view instanceof ViewGroup) { @@ -910,7 +910,7 @@ public class ViewDebug { private static void dump(View root, OutputStream clientStream) throws IOException { BufferedWriter out = null; try { - out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024); + out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024); View view = root.getRootView(); if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 6646136..763f273 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -3070,7 +3070,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; - } else if (childDimension == LayoutParams.FILL_PARENT) { + } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; @@ -3088,7 +3088,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; - } else if (childDimension == LayoutParams.FILL_PARENT) { + } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; @@ -3107,7 +3107,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; - } else if (childDimension == LayoutParams.FILL_PARENT) { + } else if (childDimension == LayoutParams.MATCH_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = 0; @@ -3362,7 +3362,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * for both width and height. For each dimension, it can specify one of: * <ul> * <li> an exact number - * <li>FILL_PARENT, which means the view wants to be as big as its parent + * <li>MATCH_PARENT, which means the view wants to be as big as its parent * (minus padding) * <li> WRAP_CONTENT, which means that the view wants to be just big enough * to enclose its content (plus padding) @@ -3376,14 +3376,22 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ public static class LayoutParams { /** - * Special value for the height or width requested by a View. - * FILL_PARENT means that the view wants to fill the available space - * within the parent, taking the parent's padding into account. + * This value has the same meaning as {@link #MATCH_PARENT} but has + * been deprecated. */ + @SuppressWarnings({"UnusedDeclaration"}) + @Deprecated public static final int FILL_PARENT = -1; /** * Special value for the height or width requested by a View. + * MATCH_PARENT means that the view wants to be as bigas its parent, + * minus the parent's padding, if any. + */ + public static final int MATCH_PARENT = -1; + + /** + * Special value for the height or width requested by a View. * WRAP_CONTENT means that the view wants to be just large enough to fit * its own internal content, taking its own padding into account. */ @@ -3391,20 +3399,20 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager /** * Information about how wide the view wants to be. Can be an exact - * size, or one of the constants FILL_PARENT or WRAP_CONTENT. + * size, or one of the constants MATCH_PARENT or WRAP_CONTENT. */ @ViewDebug.ExportedProperty(mapping = { - @ViewDebug.IntToString(from = FILL_PARENT, to = "FILL_PARENT"), + @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") }) public int width; /** * Information about how tall the view wants to be. Can be an exact - * size, or one of the constants FILL_PARENT or WRAP_CONTENT. + * size, or one of the constants MATCH_PARENT or WRAP_CONTENT. */ @ViewDebug.ExportedProperty(mapping = { - @ViewDebug.IntToString(from = FILL_PARENT, to = "FILL_PARENT"), + @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"), @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT") }) public int height; @@ -3421,9 +3429,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * * <ul> * <li><code>layout_width</code>: the width, either an exact value, - * {@link #WRAP_CONTENT} or {@link #FILL_PARENT}</li> + * {@link #WRAP_CONTENT} or {@link #MATCH_PARENT}</li> * <li><code>layout_height</code>: the height, either an exact value, - * {@link #WRAP_CONTENT} or {@link #FILL_PARENT}</li> + * {@link #WRAP_CONTENT} or {@link #MATCH_PARENT}</li> * </ul> * * @param c the application environment @@ -3442,9 +3450,9 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager * Creates a new set of layout parameters with the specified width * and height. * - * @param width the width, either {@link #FILL_PARENT}, + * @param width the width, either {@link #MATCH_PARENT}, * {@link #WRAP_CONTENT} or a fixed size in pixels - * @param height the height, either {@link #FILL_PARENT}, + * @param height the height, either {@link #MATCH_PARENT}, * {@link #WRAP_CONTENT} or a fixed size in pixels */ public LayoutParams(int width, int height) { @@ -3507,8 +3515,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (size == WRAP_CONTENT) { return "wrap-content"; } - if (size == FILL_PARENT) { - return "fill-parent"; + if (size == MATCH_PARENT) { + return "match-parent"; } return String.valueOf(size); } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 47b976b..094b7dd 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -1167,7 +1167,7 @@ public final class ViewRoot extends Handler implements ViewParent, int measureSpec; switch (rootDimension) { - case ViewGroup.LayoutParams.FILL_PARENT: + case ViewGroup.LayoutParams.MATCH_PARENT: // Window can't resize. Force root view to be windowSize. measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY); break; diff --git a/core/java/android/view/ViewStub.java b/core/java/android/view/ViewStub.java index 703a38f..d5e9af4 100644 --- a/core/java/android/view/ViewStub.java +++ b/core/java/android/view/ViewStub.java @@ -207,9 +207,11 @@ public final class ViewStub extends View { } else { throw new IllegalStateException("setVisibility called on un-referenced view"); } - } else if (visibility == VISIBLE || visibility == INVISIBLE) { + } else { super.setVisibility(visibility); - inflate(); + if (visibility == VISIBLE || visibility == INVISIBLE) { + inflate(); + } } } @@ -244,7 +246,7 @@ public final class ViewStub extends View { parent.addView(view, index); } - mInflatedViewRef = new WeakReference(view); + mInflatedViewRef = new WeakReference<View>(view); if (mInflateListener != null) { mInflateListener.onInflate(this, view); diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java index 1932765..7dd5085 100644 --- a/core/java/android/view/Window.java +++ b/core/java/android/view/Window.java @@ -484,7 +484,7 @@ public abstract class Window { /** * Set the width and height layout parameters of the window. The default - * for both of these is FILL_PARENT; you can change them to WRAP_CONTENT to + * for both of these is MATCH_PARENT; you can change them to WRAP_CONTENT to * make a window that is not full-screen. * * @param width The desired layout width of the window. diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index fe329f2..8e15f89 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -771,26 +771,26 @@ public interface WindowManager extends ViewManager { public LayoutParams() { - super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); + super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = TYPE_APPLICATION; format = PixelFormat.OPAQUE; } public LayoutParams(int _type) { - super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); + super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = _type; format = PixelFormat.OPAQUE; } public LayoutParams(int _type, int _flags) { - super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); + super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = _type; flags = _flags; format = PixelFormat.OPAQUE; } public LayoutParams(int _type, int _flags, int _format) { - super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); + super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = _type; flags = _flags; format = _format; @@ -1036,9 +1036,9 @@ public interface WindowManager extends ViewManager { sb.append(','); sb.append(y); sb.append(")("); - sb.append((width==FILL_PARENT?"fill":(width==WRAP_CONTENT?"wrap":width))); + sb.append((width== MATCH_PARENT ?"fill":(width==WRAP_CONTENT?"wrap":width))); sb.append('x'); - sb.append((height==FILL_PARENT?"fill":(height==WRAP_CONTENT?"wrap":height))); + sb.append((height== MATCH_PARENT ?"fill":(height==WRAP_CONTENT?"wrap":height))); sb.append(")"); if (softInputMode != 0) { sb.append(" sim=#"); diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index 5d3840a..bbe9c1f 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -662,6 +662,14 @@ public interface WindowManagerPolicy { public boolean finishAnimationLw(); /** + * Return true if it is okay to perform animations for an app transition + * that is about to occur. You may return false for this if, for example, + * the lock screen is currently displayed so the switch should happen + * immediately. + */ + public boolean allowAppAnimationsLw(); + + /** * Called after the screen turns off. * * @param why {@link #OFF_BECAUSE_OF_USER} or @@ -675,6 +683,11 @@ public interface WindowManagerPolicy { public void screenTurnedOn(); /** + * Return whether the screen is currently on. + */ + public boolean isScreenOn(); + + /** * Perform any initial processing of a low-level input event before the * window manager handles special keys and generates a high-level event * that is dispatched to the application. diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java index 13606e7..2aba60b 100755 --- a/core/java/android/view/WindowOrientationListener.java +++ b/core/java/android/view/WindowOrientationListener.java @@ -33,13 +33,12 @@ import android.util.Log; public abstract class WindowOrientationListener { private static final String TAG = "WindowOrientationListener"; private static final boolean DEBUG = false; - private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; + private static final boolean localLOGV = DEBUG || Config.DEBUG; private SensorManager mSensorManager; private boolean mEnabled = false; private int mRate; private Sensor mSensor; - private SensorEventListener mSensorEventListener; - private int mSensorRotation = -1; + private SensorEventListenerImpl mSensorEventListener; /** * Creates a new WindowOrientationListener. @@ -80,7 +79,6 @@ public abstract class WindowOrientationListener { } if (mEnabled == false) { if (localLOGV) Log.d(TAG, "WindowOrientationListener enabled"); - mSensorRotation = -1; mSensorManager.registerListener(mSensorEventListener, mSensor, mRate); mEnabled = true; } @@ -96,23 +94,22 @@ public abstract class WindowOrientationListener { } if (mEnabled == true) { if (localLOGV) Log.d(TAG, "WindowOrientationListener disabled"); - mSensorRotation = -1; mSensorManager.unregisterListener(mSensorEventListener); mEnabled = false; } } public int getCurrentRotation() { - return mSensorRotation; + if (mEnabled) { + return mSensorEventListener.getCurrentRotation(); + } + return -1; } class SensorEventListenerImpl implements SensorEventListener { private static final int _DATA_X = 0; private static final int _DATA_Y = 1; private static final int _DATA_Z = 2; - // Angle around x-axis thats considered almost perfect vertical to hold - // the device - private static final int PIVOT = 20; // Angle around x-asis that's considered almost too vertical. Beyond // this angle will not result in any orientation changes. f phone faces uses, // the device is leaning backward. @@ -121,30 +118,61 @@ public abstract class WindowOrientationListener { // angle will not result in any orientation changes. If phone faces uses, // the device is leaning forward. private static final int PIVOT_LOWER = -10; - // Upper threshold limit for switching from portrait to landscape - private static final int PL_UPPER = 295; - // Lower threshold limit for switching from landscape to portrait - private static final int LP_LOWER = 320; - // Lower threshold limt for switching from portrait to landscape - private static final int PL_LOWER = 270; - // Upper threshold limit for switching from landscape to portrait - private static final int LP_UPPER = 359; - // Minimum angle which is considered landscape - private static final int LANDSCAPE_LOWER = 235; - // Minimum angle which is considered portrait - private static final int PORTRAIT_LOWER = 60; - - // Internal value used for calculating linear variant - private static final float PL_LF_UPPER = - ((float)(PL_UPPER-PL_LOWER))/((float)(PIVOT_UPPER-PIVOT)); - private static final float PL_LF_LOWER = - ((float)(PL_UPPER-PL_LOWER))/((float)(PIVOT-PIVOT_LOWER)); - // Internal value used for calculating linear variant - private static final float LP_LF_UPPER = - ((float)(LP_UPPER - LP_LOWER))/((float)(PIVOT_UPPER-PIVOT)); - private static final float LP_LF_LOWER = - ((float)(LP_UPPER - LP_LOWER))/((float)(PIVOT-PIVOT_LOWER)); + static final int ROTATION_0 = 0; + static final int ROTATION_90 = 1; + static final int ROTATION_180 = 2; + static final int ROTATION_270 = 3; + int mRotation = ROTATION_0; + + // Threshold values defined for device rotation positions + // follow order ROTATION_0 .. ROTATION_270 + final int THRESHOLDS[][][] = new int[][][] { + {{60, 135}, {135, 225}, {225, 300}}, + {{0, 45}, {45, 135}, {135, 210}, {330, 360}}, + {{0, 45}, {45, 120}, {240, 315}, {315, 360}}, + {{0, 30}, {150, 225}, {225, 315}, {315, 360}} + }; + + // Transform rotation ranges based on THRESHOLDS. This + // has to be in step with THESHOLDS + final int ROTATE_TO[][] = new int[][] { + {ROTATION_270, ROTATION_180, ROTATION_90}, + {ROTATION_0, ROTATION_270, ROTATION_180, ROTATION_0}, + {ROTATION_0, ROTATION_270, ROTATION_90, ROTATION_0}, + {ROTATION_0, ROTATION_180, ROTATION_90, ROTATION_0} + }; + + // Mapping into actual Surface rotation values + final int TRANSFORM_ROTATIONS[] = new int[]{Surface.ROTATION_0, + Surface.ROTATION_90, Surface.ROTATION_180, Surface.ROTATION_270}; + + int getCurrentRotation() { + return TRANSFORM_ROTATIONS[mRotation]; + } + private void calculateNewRotation(int orientation, int zyangle) { + if (localLOGV) Log.i(TAG, orientation + ", " + zyangle + ", " + mRotation); + int rangeArr[][] = THRESHOLDS[mRotation]; + int row = -1; + for (int i = 0; i < rangeArr.length; i++) { + if ((orientation >= rangeArr[i][0]) && (orientation < rangeArr[i][1])) { + row = i; + break; + } + } + if (row != -1) { + // Find new rotation based on current rotation value. + // This also takes care of irregular rotations as well. + int rotation = ROTATE_TO[mRotation][row]; + if (localLOGV) Log.i(TAG, " new rotation = " + rotation); + if (rotation != mRotation) { + mRotation = rotation; + // Trigger orientation change + onOrientationChanged(TRANSFORM_ROTATIONS[rotation]); + } + } + } + public void onSensorChanged(SensorEvent event) { float[] values = event.values; float X = values[_DATA_X]; @@ -153,53 +181,19 @@ public abstract class WindowOrientationListener { float OneEightyOverPi = 57.29577957855f; float gravity = (float) Math.sqrt(X*X+Y*Y+Z*Z); float zyangle = (float)Math.asin(Z/gravity)*OneEightyOverPi; - int rotation = -1; if ((zyangle <= PIVOT_UPPER) && (zyangle >= PIVOT_LOWER)) { // Check orientation only if the phone is flat enough // Don't trust the angle if the magnitude is small compared to the y value float angle = (float)Math.atan2(Y, -X) * OneEightyOverPi; - int orientation = 90 - (int)Math.round(angle); + int orientation = 90 - Math.round(angle); // normalize to 0 - 359 range while (orientation >= 360) { orientation -= 360; - } + } while (orientation < 0) { orientation += 360; } - // Orientation values between LANDSCAPE_LOWER and PL_LOWER - // are considered landscape. - // Ignore orientation values between 0 and LANDSCAPE_LOWER - // For orientation values between LP_UPPER and PL_LOWER, - // the threshold gets set linearly around PIVOT. - if ((orientation >= PL_LOWER) && (orientation <= LP_UPPER)) { - float threshold; - float delta = zyangle - PIVOT; - if (mSensorRotation == Surface.ROTATION_90) { - if (delta < 0) { - // Delta is negative - threshold = LP_LOWER - (LP_LF_LOWER * delta); - } else { - threshold = LP_LOWER + (LP_LF_UPPER * delta); - } - rotation = (orientation >= threshold) ? Surface.ROTATION_0 : Surface.ROTATION_90; - } else { - if (delta < 0) { - // Delta is negative - threshold = PL_UPPER+(PL_LF_LOWER * delta); - } else { - threshold = PL_UPPER-(PL_LF_UPPER * delta); - } - rotation = (orientation <= threshold) ? Surface.ROTATION_90: Surface.ROTATION_0; - } - } else if ((orientation >= LANDSCAPE_LOWER) && (orientation < LP_LOWER)) { - rotation = Surface.ROTATION_90; - } else if ((orientation >= PL_UPPER) || (orientation <= PORTRAIT_LOWER)) { - rotation = Surface.ROTATION_0; - } - if ((rotation != -1) && (rotation != mSensorRotation)) { - mSensorRotation = rotation; - onOrientationChanged(mSensorRotation); - } + calculateNewRotation(orientation, Math.round(zyangle)); } } diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index fb369d3..6790c5d 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -90,7 +90,6 @@ class CallbackProxy extends Handler { private static final int JS_PROMPT = 114; private static final int JS_UNLOAD = 115; private static final int ASYNC_KEYEVENTS = 116; - private static final int TOO_MANY_REDIRECTS = 117; private static final int DOWNLOAD_FILE = 118; private static final int REPORT_ERROR = 119; private static final int RESEND_POST_DATA = 120; @@ -276,19 +275,6 @@ class CallbackProxy extends Handler { } break; - case TOO_MANY_REDIRECTS: - Message cancelMsg = - (Message) msg.getData().getParcelable("cancelMsg"); - Message continueMsg = - (Message) msg.getData().getParcelable("continueMsg"); - if (mWebViewClient != null) { - mWebViewClient.onTooManyRedirects(mWebView, cancelMsg, - continueMsg); - } else { - cancelMsg.sendToTarget(); - } - break; - case REPORT_ERROR: if (mWebViewClient != null) { int reasonCode = msg.arg1; @@ -790,19 +776,10 @@ class CallbackProxy extends Handler { sendMessage(msg); } + // Because this method is public and because CallbackProxy is mistakenly + // party of the public classes, we cannot remove this method. public void onTooManyRedirects(Message cancelMsg, Message continueMsg) { - // Do an unsynchronized quick check to avoid posting if no callback has - // been set. - if (mWebViewClient == null) { - cancelMsg.sendToTarget(); - return; - } - - Message msg = obtainMessage(TOO_MANY_REDIRECTS); - Bundle bundle = msg.getData(); - bundle.putParcelable("cancelMsg", cancelMsg); - bundle.putParcelable("continueMsg", continueMsg); - sendMessage(msg); + // deprecated. } public void onReceivedError(int errorCode, String description, diff --git a/core/java/android/webkit/HTML5VideoViewProxy.java b/core/java/android/webkit/HTML5VideoViewProxy.java index 3e0be1c..429b335 100644 --- a/core/java/android/webkit/HTML5VideoViewProxy.java +++ b/core/java/android/webkit/HTML5VideoViewProxy.java @@ -48,6 +48,8 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; /** * <p>Proxy for HTML5 video views. @@ -71,6 +73,9 @@ class HTML5VideoViewProxy extends Handler private static final int ENDED = 201; private static final int POSTER_FETCHED = 202; + // Timer thread -> UI thread + private static final int TIMEUPDATE = 300; + // The C++ MediaPlayerPrivateAndroid object. int mNativePointer; // The handler for WebCore thread messages; @@ -95,6 +100,22 @@ class HTML5VideoViewProxy extends Handler private static View mProgressView; // The container for the progress view and video view private static FrameLayout mLayout; + // The timer for timeupate events. + // See http://www.whatwg.org/specs/web-apps/current-work/#event-media-timeupdate + private static Timer mTimer; + private static final class TimeupdateTask extends TimerTask { + private HTML5VideoViewProxy mProxy; + + public TimeupdateTask(HTML5VideoViewProxy proxy) { + mProxy = proxy; + } + + public void run() { + mProxy.onTimeupdate(); + } + } + // The spec says the timer should fire every 250 ms or less. + private static final int TIMEUPDATE_PERIOD = 250; // ms private static final WebChromeClient.CustomViewCallback mCallback = new WebChromeClient.CustomViewCallback() { @@ -104,6 +125,8 @@ class HTML5VideoViewProxy extends Handler // which happens when the video view is detached from its parent // view. This happens in the WebChromeClient before this method // is invoked. + mTimer.cancel(); + mTimer = null; mCurrentProxy.playbackEnded(); mCurrentProxy = null; mLayout.removeView(mVideoView); @@ -118,11 +141,19 @@ class HTML5VideoViewProxy extends Handler public static void play(String url, int time, HTML5VideoViewProxy proxy, WebChromeClient client) { + if (mCurrentProxy == proxy) { + if (!mVideoView.isPlaying()) { + mVideoView.start(); + } + return; + } + if (mCurrentProxy != null) { // Some other video is already playing. Notify the caller that its playback ended. proxy.playbackEnded(); return; } + mCurrentProxy = proxy; // Create a FrameLayout that will contain the VideoView and the // progress view (if any). @@ -146,10 +177,23 @@ class HTML5VideoViewProxy extends Handler mProgressView.setVisibility(View.VISIBLE); } mLayout.setVisibility(View.VISIBLE); + mTimer = new Timer(); mVideoView.start(); client.onShowCustomView(mLayout, mCallback); } + public static boolean isPlaying(HTML5VideoViewProxy proxy) { + return (mCurrentProxy == proxy && mVideoView != null && mVideoView.isPlaying()); + } + + public static int getCurrentPosition() { + int currentPosMs = 0; + if (mVideoView != null) { + currentPosMs = mVideoView.getCurrentPosition(); + } + return currentPosMs; + } + public static void seek(int time, HTML5VideoViewProxy proxy) { if (mCurrentProxy == proxy && time >= 0 && mVideoView != null) { mVideoView.seekTo(time); @@ -159,10 +203,13 @@ class HTML5VideoViewProxy extends Handler public static void pause(HTML5VideoViewProxy proxy) { if (mCurrentProxy == proxy && mVideoView != null) { mVideoView.pause(); + mTimer.purge(); } } public static void onPrepared() { + mTimer.schedule(new TimeupdateTask(mCurrentProxy), TIMEUPDATE_PERIOD, TIMEUPDATE_PERIOD); + if (mProgressView == null || mLayout == null) { return; } @@ -203,7 +250,11 @@ class HTML5VideoViewProxy extends Handler sendMessage(obtainMessage(ENDED)); } - // Handler for the messages from WebCore thread to the UI thread. + public void onTimeupdate() { + sendMessage(obtainMessage(TIMEUPDATE)); + } + + // Handler for the messages from WebCore or Timer thread to the UI thread. @Override public void handleMessage(Message msg) { // This executes on the UI thread. @@ -241,6 +292,12 @@ class HTML5VideoViewProxy extends Handler } break; } + case TIMEUPDATE: { + if (VideoPlayer.isPlaying(this)) { + sendTimeupdate(); + } + break; + } } } @@ -410,6 +467,9 @@ class HTML5VideoViewProxy extends Handler Bitmap poster = (Bitmap) msg.obj; nativeOnPosterFetched(poster, mNativePointer); break; + case TIMEUPDATE: + nativeOnTimeupdate(msg.arg1, mNativePointer); + break; } } }; @@ -426,6 +486,12 @@ class HTML5VideoViewProxy extends Handler mWebCoreHandler.sendMessage(msg); } + private void sendTimeupdate() { + Message msg = Message.obtain(mWebCoreHandler, TIMEUPDATE); + msg.arg1 = VideoPlayer.getCurrentPosition(); + mWebCoreHandler.sendMessage(msg); + } + public Context getContext() { return mWebView.getContext(); } @@ -506,4 +572,5 @@ class HTML5VideoViewProxy extends Handler private native void nativeOnPrepared(int duration, int width, int height, int nativePointer); private native void nativeOnEnded(int nativePointer); private native void nativeOnPosterFetched(Bitmap poster, int nativePointer); + private native void nativeOnTimeupdate(int position, int nativePointer); } diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java index a182287..cf71a84 100644 --- a/core/java/android/webkit/WebStorage.java +++ b/core/java/android/webkit/WebStorage.java @@ -389,8 +389,8 @@ public final class WebStorage { mOrigins = new HashMap<String, Origin>(); for (String origin : tmp) { Origin website = new Origin(origin, - nativeGetUsageForOrigin(origin), - nativeGetQuotaForOrigin(origin)); + nativeGetQuotaForOrigin(origin), + nativeGetUsageForOrigin(origin)); mOrigins.put(origin, website); } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 6368dac..c349606 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -25,11 +25,9 @@ import android.database.DataSetObserver; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; -import android.graphics.Paint; import android.graphics.Picture; import android.graphics.Point; import android.graphics.Rect; -import android.graphics.Region; import android.graphics.drawable.Drawable; import android.net.http.SslCertificate; import android.net.Uri; @@ -55,7 +53,6 @@ import android.view.VelocityTracker; import android.view.View; import android.view.ViewConfiguration; import android.view.ViewGroup; -import android.view.ViewParent; import android.view.ViewTreeObserver; import android.view.animation.AlphaAnimation; import android.view.inputmethod.InputMethodManager; @@ -203,6 +200,8 @@ public class WebView extends AbsoluteLayout implements ViewTreeObserver.OnGlobalFocusChangeListener, ViewGroup.OnHierarchyChangeListener { + // enable debug output for drag trackers + private static final boolean DEBUG_DRAG_TRACKER = false; // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing // the screen all-the-time. Good for profiling our drawing code static private final boolean AUTO_REDRAW_HACK = false; @@ -486,7 +485,7 @@ public class WebView extends AbsoluteLayout static final int MOVE_OUT_OF_PLUGIN = 19; static final int CLEAR_TEXT_ENTRY = 20; static final int UPDATE_TEXT_SELECTION_MSG_ID = 21; - static final int UPDATE_CLIPBOARD = 22; + static final int LONG_PRESS_CENTER = 23; static final int PREVENT_TOUCH_ID = 24; static final int WEBCORE_NEED_TOUCH_EVENTS = 25; @@ -523,7 +522,7 @@ public class WebView extends AbsoluteLayout "MOVE_OUT_OF_PLUGIN", // = 19; "CLEAR_TEXT_ENTRY", // = 20; "UPDATE_TEXT_SELECTION_MSG_ID", // = 21; - "UPDATE_CLIPBOARD", // = 22; + "22", // = 22; "LONG_PRESS_CENTER", // = 23; "PREVENT_TOUCH_ID", // = 24; "WEBCORE_NEED_TOUCH_EVENTS", // = 25; @@ -1847,7 +1846,7 @@ public class WebView extends AbsoluteLayout } if (null != v) { addView(v, new AbsoluteLayout.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0)); if (mTitleShadow == null) { mTitleShadow = (Drawable) mContext.getResources().getDrawable( @@ -3666,9 +3665,6 @@ public class WebView extends AbsoluteLayout nativeHideCursor(); } - /** - * @hide - */ public void emulateShiftHeld() { if (0 == mNativeClass) return; // client isn't initialized setUpSelectXY(); @@ -3677,14 +3673,22 @@ public class WebView extends AbsoluteLayout private boolean commitCopy() { boolean copiedSomething = false; if (mExtendSelection) { - // copy region so core operates on copy without touching orig. - Region selection = new Region(nativeGetSelection()); - if (selection.isEmpty() == false) { + String selection = nativeGetSelection(); + if (selection != "") { + if (DebugFlags.WEB_VIEW) { + Log.v(LOGTAG, "commitCopy \"" + selection + "\""); + } Toast.makeText(mContext , com.android.internal.R.string.text_copied , Toast.LENGTH_SHORT).show(); - mWebViewCore.sendMessage(EventHub.GET_SELECTION, selection); copiedSomething = true; + try { + IClipboard clip = IClipboard.Stub.asInterface( + ServiceManager.getService("clipboard")); + clip.setClipboardText(selection); + } catch (android.os.RemoteException e) { + Log.e(LOGTAG, "Clipboard failed", e); + } } mExtendSelection = false; } @@ -3888,7 +3892,9 @@ public class WebView extends AbsoluteLayout public void run() { // we always force, in case our height changed, in which case we // still want to send the notification over to webkit - setNewZoomScale(mActualScale, true); + if (mWebViewCore != null) { + setNewZoomScale(mActualScale, true); + } } }); } @@ -3965,7 +3971,7 @@ public class WebView extends AbsoluteLayout mMinDY = -viewTop; mMaxDY = docBottom - viewBottom; - if (DebugFlags.DRAG_TRACKER) { + if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) { Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, " dragtracker y= " + y + " up/down= " + mMinDY + " " + mMaxDY); } @@ -4002,7 +4008,7 @@ public class WebView extends AbsoluteLayout if (mCurrStretchX != sx || mCurrStretchY != sy) { mCurrStretchX = sx; mCurrStretchY = sy; - if (DebugFlags.DRAG_TRACKER) { + if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) { Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "---- stretch " + sx + " " + sy); } @@ -4013,7 +4019,7 @@ public class WebView extends AbsoluteLayout } public void stopDrag() { - if (DebugFlags.DRAG_TRACKER) { + if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) { Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "----- stopDrag"); } mProxy.onStopDrag(); @@ -4050,7 +4056,7 @@ public class WebView extends AbsoluteLayout canvas.restoreToCount(count); return true; } - if (DebugFlags.DRAG_TRACKER) { + if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) { Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, " -- draw false " + mCurrStretchX + " " + mCurrStretchY); } @@ -4065,7 +4071,7 @@ public class WebView extends AbsoluteLayout canvas.translate(-sx, -sy); drawContent(canvas); - if (DebugFlags.DRAG_TRACKER) { + if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) { Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "--- buildBitmap " + sx + " " + sy + " " + w + " " + h); } @@ -5123,12 +5129,12 @@ public class WebView extends AbsoluteLayout mWebViewCore.sendMessageAtFrontOfQueue(EventHub.VALID_NODE_BOUNDS, motionUpData); } else { - doMotionUp(contentX, contentY, false); + doMotionUp(contentX, contentY); } } - private void doMotionUp(int contentX, int contentY, boolean useNavCache) { - if (nativeMotionUp(contentX, contentY, useNavCache ? mNavSlop : 0)) { + private void doMotionUp(int contentX, int contentY) { + if (nativeMotionUp(contentX, contentY, mNavSlop)) { if (mLogEvent) { Checkin.updateStats(mContext.getContentResolver(), Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0); @@ -5681,19 +5687,6 @@ public class WebView extends AbsoluteLayout mWebTextView.setAdapterCustom(adapter); } break; - case UPDATE_CLIPBOARD: - String str = (String) msg.obj; - if (DebugFlags.WEB_VIEW) { - Log.v(LOGTAG, "UPDATE_CLIPBOARD " + str); - } - try { - IClipboard clip = IClipboard.Stub.asInterface( - ServiceManager.getService("clipboard")); - clip.setClipboardText(str); - } catch (android.os.RemoteException e) { - Log.e(LOGTAG, "Clipboard failed", e); - } - break; case RESUME_WEBCORE_UPDATE: WebViewCore.resumeUpdate(mWebViewCore); break; @@ -5770,7 +5763,7 @@ public class WebView extends AbsoluteLayout break; case DO_MOTION_UP: - doMotionUp(msg.arg1, msg.arg2, (Boolean) msg.obj); + doMotionUp(msg.arg1, msg.arg2); break; case SHOW_FULLSCREEN: @@ -6388,7 +6381,7 @@ public class WebView extends AbsoluteLayout private native boolean nativeFocusIsPlugin(); /* package */ native int nativeFocusNodePointer(); private native Rect nativeGetCursorRingBounds(); - private native Region nativeGetSelection(); + private native String nativeGetSelection(); private native boolean nativeHasCursorNode(); private native boolean nativeHasFocusNode(); private native void nativeHideCursor(); diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java index 032295d..02c7210 100644 --- a/core/java/android/webkit/WebViewClient.java +++ b/core/java/android/webkit/WebViewClient.java @@ -86,6 +86,8 @@ public class WebViewClient { * @param view The WebView that is initiating the callback. * @param cancelMsg The message to send if the host wants to cancel * @param continueMsg The message to send if the host wants to continue + * @deprecated This method is no longer called. When the WebView encounters + * a redirect loop, it will cancel the load. */ public void onTooManyRedirects(WebView view, Message cancelMsg, Message continueMsg) { diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 885dda9..949b318 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -545,8 +545,6 @@ final class WebViewCore { */ private native void nativeSetSelection(int start, int end); - private native String nativeGetSelection(Region sel); - // Register a scheme to be treated as local scheme so that it can access // local asset files for resources private native void nativeRegisterURLSchemeAsLocal(String scheme); @@ -776,7 +774,7 @@ final class WebViewCore { "SET_BACKGROUND_COLOR", // = 126; "SET_MOVE_FOCUS", // = 127 "SAVE_DOCUMENT_STATE", // = 128; - "GET_SELECTION", // = 129; + "129", // = 129; "WEBKIT_DRAW", // = 130; "SYNC_SCROLL", // = 131; "POST_URL", // = 132; @@ -829,7 +827,7 @@ final class WebViewCore { static final int SET_BACKGROUND_COLOR = 126; static final int SET_MOVE_FOCUS = 127; static final int SAVE_DOCUMENT_STATE = 128; - static final int GET_SELECTION = 129; + static final int WEBKIT_DRAW = 130; static final int SYNC_SCROLL = 131; static final int POST_URL = 132; @@ -1266,13 +1264,6 @@ final class WebViewCore { nativeSetBackgroundColor(msg.arg1); break; - case GET_SELECTION: - String str = nativeGetSelection((Region) msg.obj); - Message.obtain(mWebView.mPrivateHandler - , WebView.UPDATE_CLIPBOARD, str) - .sendToTarget(); - break; - case DUMP_DOMTREE: nativeDumpDomTree(msg.arg1 == 1); break; @@ -1325,13 +1316,14 @@ final class WebViewCore { case VALID_NODE_BOUNDS: { MotionUpData motionUpData = (MotionUpData) msg.obj; - boolean result = nativeValidNodeAndBounds( + if (!nativeValidNodeAndBounds( motionUpData.mFrame, motionUpData.mNode, - motionUpData.mBounds); + motionUpData.mBounds)) { + nativeUpdateFrameCache(); + } Message message = mWebView.mPrivateHandler .obtainMessage(WebView.DO_MOTION_UP, - motionUpData.mX, motionUpData.mY, - new Boolean(result)); + motionUpData.mX, motionUpData.mY); mWebView.mPrivateHandler.sendMessageAtFrontOfQueue( message); break; diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 92ff315..e241c77 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -127,11 +127,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te static final int TOUCH_MODE_FLING = 4; /** - * Indicates that the user is currently dragging the fast scroll thumb - */ - static final int TOUCH_MODE_FAST_SCROLL = 5; - - /** * Regular layout - usually an unsolicited layout from the view system */ static final int LAYOUT_NORMAL = 0; @@ -440,6 +435,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te private Runnable mClearScrollingCache; private int mMinimumVelocity; private int mMaximumVelocity; + + final boolean[] mIsScrap = new boolean[1]; /** * Interface definition for a callback to be invoked when the list or grid @@ -1239,9 +1236,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te * converting an old view or making a new one. * * @param position The position to display + * @param isScrap Array of at least 1 boolean, the first entry will become true if + * the returned view was taken from the scrap heap, false if otherwise. + * * @return A view displaying the data associated with the specified position */ - View obtainView(int position) { + View obtainView(int position, boolean[] isScrap) { + isScrap[0] = false; View scrapView; scrapView = mRecycler.getScrapView(position); @@ -1269,6 +1270,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te ViewDebug.trace(scrapView, ViewDebug.RecyclerTraceType.MOVE_TO_SCRAP_HEAP, position, -1); } + } else { + isScrap[0] = true; } } else { child = mAdapter.getView(position, null, this); @@ -1543,6 +1546,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te // Dismiss the popup in case onSaveInstanceState() was not invoked dismissPopup(); + // Detach any view left in the scrap heap + mRecycler.clear(); + final ViewTreeObserver treeObserver = getViewTreeObserver(); if (treeObserver != null) { treeObserver.removeOnTouchModeChangeListener(this); @@ -1636,6 +1642,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mDataChanged) return; if (mAdapter != null && mItemCount > 0 && + mClickMotionPosition != INVALID_POSITION && mClickMotionPosition < mAdapter.getCount() && sameWindow()) { performItemClick(mChild, mClickMotionPosition, getAdapter().getItemId( mClickMotionPosition)); @@ -2969,7 +2976,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te break; case KeyEvent.KEYCODE_SPACE: // Only send spaces once we are filtered - okToSend = mFiltered = true; + okToSend = mFiltered; break; } @@ -3594,12 +3601,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te for (int i = 0; i < count; ++i) { final View victim = activeViews[i]; if (victim != null) { - int whichScrap = ((AbsListView.LayoutParams) - victim.getLayoutParams()).viewType; + int whichScrap = ((AbsListView.LayoutParams) victim.getLayoutParams()).viewType; activeViews[i] = null; if (whichScrap == AdapterView.ITEM_VIEW_TYPE_IGNORE) { + removeDetachedView(victim, false); // Do not move views that should be ignored continue; } diff --git a/core/java/android/widget/AbsSeekBar.java b/core/java/android/widget/AbsSeekBar.java index d25530b..d6dd872 100644 --- a/core/java/android/widget/AbsSeekBar.java +++ b/core/java/android/widget/AbsSeekBar.java @@ -26,7 +26,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; public abstract class AbsSeekBar extends ProgressBar { - private Drawable mThumb; private int mThumbOffset; @@ -66,8 +65,9 @@ public abstract class AbsSeekBar extends ProgressBar { Drawable thumb = a.getDrawable(com.android.internal.R.styleable.SeekBar_thumb); setThumb(thumb); // will guess mThumbOffset if thumb != null... // ...but allow layout to override this - int thumbOffset = - a.getDimensionPixelOffset(com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset()); + int thumbOffset = a.getDimensionPixelOffset( + com.android.internal.R.styleable.SeekBar_thumbOffset, getThumbOffset()); + setThumbOffset(thumbOffset); a.recycle(); a = context.obtainStyledAttributes(attrs, @@ -91,7 +91,7 @@ public abstract class AbsSeekBar extends ProgressBar { // Assuming the thumb drawable is symmetric, set the thumb offset // such that the thumb will hang halfway off either edge of the // progress bar. - mThumbOffset = (int)thumb.getIntrinsicWidth() / 2; + mThumbOffset = thumb.getIntrinsicWidth() / 2; } mThumb = thumb; invalidate(); @@ -368,20 +368,21 @@ public abstract class AbsSeekBar extends ProgressBar { @Override public boolean onKeyDown(int keyCode, KeyEvent event) { - int progress = getProgress(); - - switch (keyCode) { - case KeyEvent.KEYCODE_DPAD_LEFT: - if (progress <= 0) break; - setProgress(progress - mKeyProgressIncrement, true); - onKeyChange(); - return true; - - case KeyEvent.KEYCODE_DPAD_RIGHT: - if (progress >= getMax()) break; - setProgress(progress + mKeyProgressIncrement, true); - onKeyChange(); - return true; + if (isEnabled()) { + int progress = getProgress(); + switch (keyCode) { + case KeyEvent.KEYCODE_DPAD_LEFT: + if (progress <= 0) break; + setProgress(progress - mKeyProgressIncrement, true); + onKeyChange(); + return true; + + case KeyEvent.KEYCODE_DPAD_RIGHT: + if (progress >= getMax()) break; + setProgress(progress + mKeyProgressIncrement, true); + onKeyChange(); + return true; + } } return super.onKeyDown(keyCode, event); diff --git a/core/java/android/widget/AbsSpinner.java b/core/java/android/widget/AbsSpinner.java index 424a936..c939e3f 100644 --- a/core/java/android/widget/AbsSpinner.java +++ b/core/java/android/widget/AbsSpinner.java @@ -249,7 +249,7 @@ public abstract class AbsSpinner extends AdapterView<SpinnerAdapter> { @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } diff --git a/core/java/android/widget/AbsoluteLayout.java b/core/java/android/widget/AbsoluteLayout.java index c77f7ae..b829655 100644 --- a/core/java/android/widget/AbsoluteLayout.java +++ b/core/java/android/widget/AbsoluteLayout.java @@ -161,9 +161,9 @@ public class AbsoluteLayout extends ViewGroup { * Creates a new set of layout parameters with the specified width, * height and location. * - * @param width the width, either {@link #FILL_PARENT}, + * @param width the width, either {@link #MATCH_PARENT}, {@link #WRAP_CONTENT} or a fixed size in pixels - * @param height the height, either {@link #FILL_PARENT}, + * @param height the height, either {@link #MATCH_PARENT}, {@link #WRAP_CONTENT} or a fixed size in pixels * @param x the X location of the child * @param y the Y location of the child diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index ce985e3..bb9a672 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -172,7 +172,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mDropDownAnchorId = a.getResourceId(R.styleable.AutoCompleteTextView_dropDownAnchor, View.NO_ID); - // For dropdown width, the developer can specify a specific width, or FILL_PARENT + // For dropdown width, the developer can specify a specific width, or MATCH_PARENT // (for full screen width) or WRAP_CONTENT (to match the width of the anchored view). mDropDownWidth = a.getLayoutDimension(R.styleable.AutoCompleteTextView_dropDownWidth, ViewGroup.LayoutParams.WRAP_CONTENT); @@ -240,7 +240,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe /** * <p>Returns the current width for the auto-complete drop down list. This can - * be a fixed width, or {@link ViewGroup.LayoutParams#FILL_PARENT} to fill the screen, or + * be a fixed width, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill the screen, or * {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the width of its anchor view.</p> * * @return the width for the drop down list @@ -253,7 +253,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe /** * <p>Sets the current width for the auto-complete drop down list. This can - * be a fixed width, or {@link ViewGroup.LayoutParams#FILL_PARENT} to fill the screen, or + * be a fixed width, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill the screen, or * {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the width of its anchor view.</p> * * @param width the width to use @@ -266,7 +266,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe /** * <p>Returns the current height for the auto-complete drop down list. This can - * be a fixed height, or {@link ViewGroup.LayoutParams#FILL_PARENT} to fill + * be a fixed height, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the height * of the drop down's content.</p> * @@ -280,7 +280,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe /** * <p>Sets the current height for the auto-complete drop down list. This can - * be a fixed height, or {@link ViewGroup.LayoutParams#FILL_PARENT} to fill + * be a fixed height, or {@link ViewGroup.LayoutParams#MATCH_PARENT} to fill * the screen, or {@link ViewGroup.LayoutParams#WRAP_CONTENT} to fit the height * of the drop down's content.</p> * @@ -1129,7 +1129,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe boolean noInputMethod = isInputMethodNotNeeded(); if (mPopup.isShowing()) { - if (mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT) { + if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) { // The call to PopupWindow's update method below can accept -1 for any // value you do not want to update. widthSpec = -1; @@ -1139,19 +1139,19 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe widthSpec = mDropDownWidth; } - if (mDropDownHeight == ViewGroup.LayoutParams.FILL_PARENT) { + if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) { // The call to PopupWindow's update method below can accept -1 for any // value you do not want to update. - heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.FILL_PARENT; + heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT; if (noInputMethod) { mPopup.setWindowLayoutMode( - mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT ? - ViewGroup.LayoutParams.FILL_PARENT : 0, 0); + mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ? + ViewGroup.LayoutParams.MATCH_PARENT : 0, 0); } else { mPopup.setWindowLayoutMode( - mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT ? - ViewGroup.LayoutParams.FILL_PARENT : 0, - ViewGroup.LayoutParams.FILL_PARENT); + mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ? + ViewGroup.LayoutParams.MATCH_PARENT : 0, + ViewGroup.LayoutParams.MATCH_PARENT); } } else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { heightSpec = height; @@ -1164,8 +1164,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mPopup.update(getDropDownAnchorView(), mDropDownHorizontalOffset, mDropDownVerticalOffset, widthSpec, heightSpec); } else { - if (mDropDownWidth == ViewGroup.LayoutParams.FILL_PARENT) { - widthSpec = ViewGroup.LayoutParams.FILL_PARENT; + if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) { + widthSpec = ViewGroup.LayoutParams.MATCH_PARENT; } else { if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) { mPopup.setWidth(getDropDownAnchorView().getWidth()); @@ -1174,8 +1174,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } } - if (mDropDownHeight == ViewGroup.LayoutParams.FILL_PARENT) { - heightSpec = ViewGroup.LayoutParams.FILL_PARENT; + if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) { + heightSpec = ViewGroup.LayoutParams.MATCH_PARENT; } else { if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) { mPopup.setHeight(height); @@ -1295,7 +1295,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe hintContainer.setOrientation(LinearLayout.VERTICAL); LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, 0, 1.0f + ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f ); hintContainer.addView(dropDownView, hintParams); hintContainer.addView(hintView); @@ -1331,7 +1331,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe final int maxHeight = mPopup.getMaxAvailableHeight( getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations); - if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.FILL_PARENT) { + if (mDropDownAlwaysVisible || mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) { // getMaxAvailableHeight() subtracts the padding, so we put it back, // to get the available height for the whole window int padding = 0; @@ -1485,8 +1485,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * @return the view for the specified item */ @Override - protected View obtainView(int position) { - View view = super.obtainView(position); + View obtainView(int position, boolean[] isScrap) { + View view = super.obtainView(position, isScrap); if (view instanceof TextView) { ((TextView) view).setHorizontallyScrolling(true); diff --git a/core/java/android/widget/FrameLayout.java b/core/java/android/widget/FrameLayout.java index 3afd5d4..65a4673 100644 --- a/core/java/android/widget/FrameLayout.java +++ b/core/java/android/widget/FrameLayout.java @@ -164,12 +164,12 @@ public class FrameLayout extends ViewGroup { /** * Returns a set of layout parameters with a width of - * {@link android.view.ViewGroup.LayoutParams#FILL_PARENT}, - * and a height of {@link android.view.ViewGroup.LayoutParams#FILL_PARENT}. + * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}, + * and a height of {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}. */ @Override protected LayoutParams generateDefaultLayoutParams() { - return new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); + return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); } /** @@ -467,9 +467,9 @@ public class FrameLayout extends ViewGroup { * Creates a new set of layout parameters with the specified width, height * and weight. * - * @param width the width, either {@link #FILL_PARENT}, + * @param width the width, either {@link #MATCH_PARENT}, * {@link #WRAP_CONTENT} or a fixed size in pixels - * @param height the height, either {@link #FILL_PARENT}, + * @param height the height, either {@link #MATCH_PARENT}, * {@link #WRAP_CONTENT} or a fixed size in pixels * @param gravity the gravity * diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index ffe9908..30a38df 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -931,11 +931,11 @@ public class GridView extends AbsListView { mItemCount = mAdapter == null ? 0 : mAdapter.getCount(); final int count = mItemCount; if (count > 0) { - final View child = obtainView(0); + final View child = obtainView(0, mIsScrap); AbsListView.LayoutParams p = (AbsListView.LayoutParams)child.getLayoutParams(); if (p == null) { - p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0); child.setLayoutParams(p); } @@ -1203,7 +1203,7 @@ public class GridView extends AbsListView { View child; if (!mDataChanged) { - // Try to use an exsiting view for this position + // Try to use an existing view for this position child = mRecycler.getActiveView(position); if (child != null) { // Found it -- we're using an existing child @@ -1215,10 +1215,10 @@ public class GridView extends AbsListView { // Make a new view for this position, or convert an unused view if // possible - child = obtainView(position); + child = obtainView(position, mIsScrap); // This needs to be positioned and measured - setupChild(child, position, y, flow, childrenLeft, selected, false, where); + setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0], where); return child; } @@ -1254,7 +1254,7 @@ public class GridView extends AbsListView { // some up... AbsListView.LayoutParams p = (AbsListView.LayoutParams)child.getLayoutParams(); if (p == null) { - p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0); } p.viewType = mAdapter.getItemViewType(position); diff --git a/core/java/android/widget/ImageButton.java b/core/java/android/widget/ImageButton.java index d417e40..5c05170 100644 --- a/core/java/android/widget/ImageButton.java +++ b/core/java/android/widget/ImageButton.java @@ -44,11 +44,11 @@ import java.util.Map; * <pre> * <?xml version="1.0" encoding="utf-8"?> * <selector xmlns:android="http://schemas.android.com/apk/res/android"> - * <item android:drawable="@drawable/button_normal" /> <!-- default --> * <item android:state_pressed="true" * android:drawable="@drawable/button_pressed" /> <!-- pressed --> * <item android:state_focused="true" * android:drawable="@drawable/button_focused" /> <!-- focused --> + * <item android:drawable="@drawable/button_normal" /> <!-- default --> * </selector></pre> * * <p>Save the XML file in your project {@code res/drawable/} folder and then @@ -57,6 +57,11 @@ import java.util.Map; * based on the state of the button and the corresponding images * defined in the XML.</p> * + * <p>The order of the {@code <item>} elements is important because they are + * evaluated in order. This is why the "normal" button image comes last, because + * it will only be applied after {@code android:state_pressed} and {@code + * android:state_focused} have both evaluated false.</p> + * * <p><strong>XML attributes</strong></p> * <p> * See {@link android.R.styleable#ImageView Button Attributes}, diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java index 8f24041..3853359 100644 --- a/core/java/android/widget/ImageView.java +++ b/core/java/android/widget/ImageView.java @@ -491,8 +491,16 @@ public class ImageView extends View { } } else if (mUri != null) { String scheme = mUri.getScheme(); - if (ContentResolver.SCHEME_CONTENT.equals(scheme) - || ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme) + if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) { + try { + // Load drawable through Resources, to get the source density information + ContentResolver.OpenResourceIdResult r = + mContext.getContentResolver().getResourceId(mUri); + d = r.r.getDrawable(r.id); + } catch (Exception e) { + Log.w("ImageView", "Unable to open content: " + mUri, e); + } + } else if (ContentResolver.SCHEME_CONTENT.equals(scheme) || ContentResolver.SCHEME_FILE.equals(scheme)) { try { d = Drawable.createFromStream( diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index 6cc794b..37372c5 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -378,7 +378,7 @@ public class LinearLayout extends ViewGroup { } boolean matchWidthLocally = false; - if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.FILL_PARENT) { + if (widthMode != MeasureSpec.EXACTLY && lp.width == LayoutParams.MATCH_PARENT) { // The width of the linear layout will scale, and at least one // child said it wanted to match our width. Set a flag // indicating that we need to remeasure at least that view when @@ -391,7 +391,7 @@ public class LinearLayout extends ViewGroup { final int measuredWidth = child.getMeasuredWidth() + margin; maxWidth = Math.max(maxWidth, measuredWidth); - allFillParent = allFillParent && lp.width == LayoutParams.FILL_PARENT; + allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; if (lp.weight > 0) { /* * Widths of weighted Views are bogus if we end up @@ -472,12 +472,12 @@ public class LinearLayout extends ViewGroup { maxWidth = Math.max(maxWidth, measuredWidth); boolean matchWidthLocally = widthMode != MeasureSpec.EXACTLY && - lp.width == LayoutParams.FILL_PARENT; + lp.width == LayoutParams.MATCH_PARENT; alternativeMaxWidth = Math.max(alternativeMaxWidth, matchWidthLocally ? margin : measuredWidth); - allFillParent = allFillParent && lp.width == LayoutParams.FILL_PARENT; + allFillParent = allFillParent && lp.width == LayoutParams.MATCH_PARENT; mTotalLength += child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin + getNextLocationOffset(child); @@ -515,7 +515,7 @@ public class LinearLayout extends ViewGroup { if (child.getVisibility() != GONE) { LinearLayout.LayoutParams lp = ((LinearLayout.LayoutParams)child.getLayoutParams()); - if (lp.width == LayoutParams.FILL_PARENT) { + if (lp.width == LayoutParams.MATCH_PARENT) { // Temporarily force children to reuse their old measured height // FIXME: this may not be right for something like wrapping text? int oldHeight = lp.height; @@ -629,7 +629,7 @@ public class LinearLayout extends ViewGroup { } boolean matchHeightLocally = false; - if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.FILL_PARENT) { + if (heightMode != MeasureSpec.EXACTLY && lp.height == LayoutParams.MATCH_PARENT) { // The height of the linear layout will scale, and at least one // child said it wanted to match our height. Set a flag indicating that // we need to remeasure at least that view when we know our height. @@ -657,7 +657,7 @@ public class LinearLayout extends ViewGroup { maxHeight = Math.max(maxHeight, childHeight); - allFillParent = allFillParent && lp.height == LayoutParams.FILL_PARENT; + allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT; if (lp.weight > 0) { /* * Heights of weighted Views are bogus if we end up @@ -758,7 +758,7 @@ public class LinearLayout extends ViewGroup { lp.rightMargin + getNextLocationOffset(child); boolean matchHeightLocally = heightMode != MeasureSpec.EXACTLY && - lp.height == LayoutParams.FILL_PARENT; + lp.height == LayoutParams.MATCH_PARENT; final int margin = lp.topMargin + lp .bottomMargin; int childHeight = child.getMeasuredHeight() + margin; @@ -766,7 +766,7 @@ public class LinearLayout extends ViewGroup { alternativeMaxHeight = Math.max(alternativeMaxHeight, matchHeightLocally ? margin : childHeight); - allFillParent = allFillParent && lp.height == LayoutParams.FILL_PARENT; + allFillParent = allFillParent && lp.height == LayoutParams.MATCH_PARENT; if (baselineAligned) { final int childBaseline = child.getBaseline(); @@ -832,7 +832,7 @@ public class LinearLayout extends ViewGroup { if (child.getVisibility() != GONE) { LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); - if (lp.height == LayoutParams.FILL_PARENT) { + if (lp.height == LayoutParams.MATCH_PARENT) { // Temporarily force children to reuse their old measured width // FIXME: this may not be right for something like wrapping text? int oldWidth = lp.width; @@ -991,6 +991,9 @@ public class LinearLayout extends ViewGroup { case Gravity.RIGHT: childLeft = childRight - childWidth - lp.rightMargin; break; + default: + childLeft = paddingLeft; + break; } @@ -1062,7 +1065,7 @@ public class LinearLayout extends ViewGroup { final LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) child.getLayoutParams(); - if (baselineAligned && lp.height != LayoutParams.FILL_PARENT) { + if (baselineAligned && lp.height != LayoutParams.MATCH_PARENT) { childBaseline = child.getBaseline(); } @@ -1102,6 +1105,9 @@ public class LinearLayout extends ViewGroup { childTop -= (maxDescent[INDEX_BOTTOM] - descent); } break; + default: + childTop = paddingTop; + break; } childLeft += lp.leftMargin; @@ -1193,7 +1199,7 @@ public class LinearLayout extends ViewGroup { /** * Returns a set of layout parameters with a width of - * {@link android.view.ViewGroup.LayoutParams#FILL_PARENT} + * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} * when the layout's orientation is {@link #VERTICAL}. When the orientation is * {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT} @@ -1204,7 +1210,7 @@ public class LinearLayout extends ViewGroup { if (mOrientation == HORIZONTAL) { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } else if (mOrientation == VERTICAL) { - return new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); + return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); } return null; } @@ -1284,9 +1290,9 @@ public class LinearLayout extends ViewGroup { * Creates a new set of layout parameters with the specified width, height * and weight. * - * @param width the width, either {@link #FILL_PARENT}, + * @param width the width, either {@link #MATCH_PARENT}, * {@link #WRAP_CONTENT} or a fixed size in pixels - * @param height the height, either {@link #FILL_PARENT}, + * @param height the height, either {@link #MATCH_PARENT}, * {@link #WRAP_CONTENT} or a fixed size in pixels * @param weight the weight */ diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index b574d45..c63774a 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1033,7 +1033,7 @@ public class ListView extends AbsListView { mItemCount = mAdapter == null ? 0 : mAdapter.getCount(); if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED)) { - final View child = obtainView(0); + final View child = obtainView(0, mIsScrap); measureScrapChild(child, 0, widthMeasureSpec); @@ -1067,7 +1067,7 @@ public class ListView extends AbsListView { private void measureScrapChild(View child, int position, int widthMeasureSpec) { LayoutParams p = (LayoutParams) child.getLayoutParams(); if (p == null) { - p = new LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + p = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0); child.setLayoutParams(p); } @@ -1142,9 +1142,10 @@ public class ListView extends AbsListView { endPosition = (endPosition == NO_POSITION) ? adapter.getCount() - 1 : endPosition; final AbsListView.RecycleBin recycleBin = mRecycler; final boolean recyle = recycleOnMeasure(); + final boolean[] isScrap = mIsScrap; for (i = startPosition; i <= endPosition; ++i) { - child = obtainView(i); + child = obtainView(i, isScrap); measureScrapChild(child, i, widthMeasureSpec); @@ -1665,10 +1666,10 @@ public class ListView extends AbsListView { } // Make a new view for this position, or convert an unused view if possible - child = obtainView(position); + child = obtainView(position, mIsScrap); // This needs to be positioned and measured - setupChild(child, position, y, flow, childrenLeft, selected, false); + setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0]); return child; } @@ -1701,7 +1702,7 @@ public class ListView extends AbsListView { // noinspection unchecked AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams(); if (p == null) { - p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, + p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, 0); } p.viewType = mAdapter.getItemViewType(position); @@ -2387,7 +2388,7 @@ public class ListView extends AbsListView { ViewGroup.LayoutParams p = child.getLayoutParams(); if (p == null) { p = new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT); } @@ -2823,17 +2824,19 @@ public class ListView extends AbsListView { private View addViewAbove(View theView, int position) { int abovePosition = position - 1; - View view = obtainView(abovePosition); + View view = obtainView(abovePosition, mIsScrap); int edgeOfNewChild = theView.getTop() - mDividerHeight; - setupChild(view, abovePosition, edgeOfNewChild, false, mListPadding.left, false, false); + setupChild(view, abovePosition, edgeOfNewChild, false, mListPadding.left, + false, mIsScrap[0]); return view; } private View addViewBelow(View theView, int position) { int belowPosition = position + 1; - View view = obtainView(belowPosition); + View view = obtainView(belowPosition, mIsScrap); int edgeOfNewChild = theView.getBottom() + mDividerHeight; - setupChild(view, belowPosition, edgeOfNewChild, true, mListPadding.left, false, false); + setupChild(view, belowPosition, edgeOfNewChild, true, mListPadding.left, + false, mIsScrap[0]); return view; } @@ -3080,13 +3083,19 @@ public class ListView extends AbsListView { if (gainFocus && previouslyFocusedRect != null) { previouslyFocusedRect.offset(mScrollX, mScrollY); + final ListAdapter adapter = mAdapter; + final int firstPosition = mFirstPosition; + // Don't cache the result of getChildCount here, it could change in layoutChildren. + if (adapter.getCount() < getChildCount() + firstPosition) { + mLayoutMode = LAYOUT_NORMAL; + layoutChildren(); + } + // figure out which item should be selected based on previously // focused rect Rect otherRect = mTempRect; int minDistance = Integer.MAX_VALUE; final int childCount = getChildCount(); - final int firstPosition = mFirstPosition; - final ListAdapter adapter = mAdapter; for (int i = 0; i < childCount; i++) { // only consider selectable views diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java index 446a992..c246c247 100644 --- a/core/java/android/widget/MediaController.java +++ b/core/java/android/widget/MediaController.java @@ -167,8 +167,8 @@ public class MediaController extends FrameLayout { mAnchor = view; FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.FILL_PARENT + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT ); removeAllViews(); diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index e4cc609..d20ab3b 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -569,7 +569,7 @@ public class PopupWindow { * the current width or height is requested as an explicit size from * the window manager. You can supply * {@link ViewGroup.LayoutParams#WRAP_CONTENT} or - * {@link ViewGroup.LayoutParams#FILL_PARENT} to have that measure + * {@link ViewGroup.LayoutParams#MATCH_PARENT} to have that measure * spec supplied instead, replacing the absolute width and height that * has been set in the popup.</p> * @@ -578,11 +578,11 @@ public class PopupWindow { * * @param widthSpec an explicit width measure spec mode, either * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, - * {@link ViewGroup.LayoutParams#FILL_PARENT}, or 0 to use the absolute + * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute * width. * @param heightSpec an explicit height measure spec mode, either * {@link ViewGroup.LayoutParams#WRAP_CONTENT}, - * {@link ViewGroup.LayoutParams#FILL_PARENT}, or 0 to use the absolute + * {@link ViewGroup.LayoutParams#MATCH_PARENT}, or 0 to use the absolute * height. */ public void setWindowLayoutMode(int widthSpec, int heightSpec) { @@ -785,7 +785,7 @@ public class PopupWindow { if (mBackground != null) { final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams(); - int height = ViewGroup.LayoutParams.FILL_PARENT; + int height = ViewGroup.LayoutParams.MATCH_PARENT; if (layoutParams != null && layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT) { height = ViewGroup.LayoutParams.WRAP_CONTENT; @@ -795,7 +795,7 @@ public class PopupWindow { // within another view that owns the background drawable PopupViewContainer popupViewContainer = new PopupViewContainer(mContext); PopupViewContainer.LayoutParams listParams = new PopupViewContainer.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, height + ViewGroup.LayoutParams.MATCH_PARENT, height ); popupViewContainer.setBackgroundDrawable(mBackground); popupViewContainer.addView(mContentView, listParams); diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java index e19a93d..1aa1df3 100644 --- a/core/java/android/widget/RelativeLayout.java +++ b/core/java/android/widget/RelativeLayout.java @@ -45,8 +45,7 @@ import java.util.ArrayList; /** * A Layout where the positions of the children can be described in relation to each other or to the - * parent. For the sake of efficiency, the relations between views are evaluated in one pass, so if - * view Y is dependent on the position of view X, make sure the view X comes first in the layout. + * parent. * * <p> * Note that you cannot have a circular dependency between the size of the RelativeLayout and the @@ -292,6 +291,8 @@ public class RelativeLayout extends ViewGroup { } } + // TODO: we need to find another way to implement RelativeLayout + // This implementation cannot handle every case @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mDirtyHierarchy) { @@ -439,6 +440,10 @@ public class RelativeLayout extends ViewGroup { final int[] rules = params.getRules(); if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_HORIZONTAL] != 0) { centerHorizontal(child, params, width); + } else if (rules[ALIGN_PARENT_RIGHT] != 0) { + final int childWidth = child.getMeasuredWidth(); + params.mLeft = width - mPaddingRight - childWidth; + params.mRight = params.mLeft + childWidth; } } } @@ -465,6 +470,10 @@ public class RelativeLayout extends ViewGroup { final int[] rules = params.getRules(); if (rules[CENTER_IN_PARENT] != 0 || rules[CENTER_VERTICAL] != 0) { centerVertical(child, params, height); + } else if (rules[ALIGN_PARENT_BOTTOM] != 0) { + final int childHeight = child.getMeasuredHeight(); + params.mTop = height - mPaddingBottom - childHeight; + params.mBottom = params.mTop + childHeight; } } } @@ -561,7 +570,7 @@ public class RelativeLayout extends ViewGroup { mPaddingLeft, mPaddingRight, myWidth); int childHeightMeasureSpec; - if (params.width == LayoutParams.FILL_PARENT) { + if (params.width == LayoutParams.MATCH_PARENT) { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.EXACTLY); } else { childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(myHeight, MeasureSpec.AT_MOST); @@ -623,7 +632,7 @@ public class RelativeLayout extends ViewGroup { // We can grow in this dimension. childSpecSize = childSize; } - } else if (childSize == LayoutParams.FILL_PARENT) { + } else if (childSize == LayoutParams.MATCH_PARENT) { // Child wanted to be as big as possible. Give all availble // space childSpecMode = MeasureSpec.EXACTLY; @@ -674,7 +683,7 @@ public class RelativeLayout extends ViewGroup { params.mRight = params.mLeft + child.getMeasuredWidth(); } } - return false; + return rules[ALIGN_PARENT_RIGHT] != 0; } private boolean positionChildVertical(View child, LayoutParams params, int myHeight, @@ -703,7 +712,7 @@ public class RelativeLayout extends ViewGroup { params.mBottom = params.mTop + child.getMeasuredHeight(); } } - return false; + return rules[ALIGN_PARENT_BOTTOM] != 0; } private void applyHorizontalSizeRules(LayoutParams childParams, int myWidth) { diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index b847e57..3003580 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -26,6 +26,7 @@ import android.graphics.PorterDuff; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.net.Uri; +import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; @@ -37,7 +38,6 @@ import android.view.ViewGroup; import android.view.LayoutInflater.Filter; import android.view.View.OnClickListener; -import java.lang.Class; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -137,11 +137,21 @@ public class RemoteViews implements Parcelable, Filter { if (target != null && pendingIntent != null) { OnClickListener listener = new OnClickListener() { public void onClick(View v) { - int[] pos = new int[2]; + // Find target view location in screen coordinates and + // fill into PendingIntent before sending. + final float appScale = v.getContext().getResources() + .getCompatibilityInfo().applicationScale; + final int[] pos = new int[2]; v.getLocationOnScreen(pos); - Intent intent = new Intent(); - intent.setSourceBounds(new Rect(pos[0], pos[1], - pos[0]+v.getWidth(), pos[1]+v.getHeight())); + + final Rect rect = new Rect(); + rect.left = (int) (pos[0] * appScale + 0.5f); + rect.top = (int) (pos[1] * appScale + 0.5f); + rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f); + rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f); + + final Intent intent = new Intent(); + intent.setSourceBounds(rect); try { // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? v.getContext().startIntentSender( @@ -273,6 +283,7 @@ public class RemoteViews implements Parcelable, Filter { static final int CHAR_SEQUENCE = 10; static final int URI = 11; static final int BITMAP = 12; + static final int BUNDLE = 13; int viewId; String methodName; @@ -332,6 +343,9 @@ public class RemoteViews implements Parcelable, Filter { case BITMAP: this.value = Bitmap.CREATOR.createFromParcel(in); break; + case BUNDLE: + this.value = in.readBundle(); + break; default: break; } @@ -384,6 +398,9 @@ public class RemoteViews implements Parcelable, Filter { case BITMAP: ((Bitmap)this.value).writeToParcel(out, flags); break; + case BUNDLE: + out.writeBundle((Bundle) this.value); + break; default: break; } @@ -415,6 +432,8 @@ public class RemoteViews implements Parcelable, Filter { return Uri.class; case BITMAP: return Bitmap.class; + case BUNDLE: + return Bundle.class; default: return null; } @@ -876,6 +895,17 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Call a method taking one Bundle on a view in the layout for this RemoteViews. + * + * @param viewId The id of the view whose text should change + * @param methodName The name of the method to call. + * @param value The value to pass to the method. + */ + public void setBundle(int viewId, String methodName, Bundle value) { + addAction(new ReflectionAction(viewId, methodName, ReflectionAction.BUNDLE, value)); + } + + /** * Inflates the view hierarchy represented by this object and applies * all of the actions. * diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java index 9dd4d15..479965a 100644 --- a/core/java/android/widget/SimpleAdapter.java +++ b/core/java/android/widget/SimpleAdapter.java @@ -25,7 +25,6 @@ import android.net.Uri; import java.util.ArrayList; import java.util.List; import java.util.Map; -import java.util.WeakHashMap; /** * An easy adapter to map static data to views defined in an XML file. You can specify the data @@ -58,7 +57,6 @@ public class SimpleAdapter extends BaseAdapter implements Filterable { private int mResource; private int mDropDownResource; private LayoutInflater mInflater; - private final WeakHashMap<View, View[]> mHolders = new WeakHashMap<View, View[]>(); private SimpleFilter mFilter; private ArrayList<Map<String, ?>> mUnfilteredData; @@ -121,16 +119,6 @@ public class SimpleAdapter extends BaseAdapter implements Filterable { View v; if (convertView == null) { v = mInflater.inflate(resource, parent, false); - - final int[] to = mTo; - final int count = to.length; - final View[] holder = new View[count]; - - for (int i = 0; i < count; i++) { - holder[i] = v.findViewById(to[i]); - } - - mHolders.put(v, holder); } else { v = convertView; } @@ -162,13 +150,12 @@ public class SimpleAdapter extends BaseAdapter implements Filterable { } final ViewBinder binder = mViewBinder; - final View[] holder = mHolders.get(view); final String[] from = mFrom; final int[] to = mTo; final int count = to.length; for (int i = 0; i < count; i++) { - final View v = holder[i]; + final View v = view.findViewById(to[i]); if (v != null) { final Object data = dataSet.get(from[i]); String text = data == null ? "" : data.toString(); @@ -187,7 +174,8 @@ public class SimpleAdapter extends BaseAdapter implements Filterable { ((Checkable) v).setChecked((Boolean) data); } else { throw new IllegalStateException(v.getClass().getName() + - " should be bound to a Boolean, not a " + data.getClass()); + " should be bound to a Boolean, not a " + + (data == null ? "<unknown type>" : data.getClass())); } } else if (v instanceof TextView) { // Note: keep the instanceof TextView check at the bottom of these diff --git a/core/java/android/widget/SimpleCursorAdapter.java b/core/java/android/widget/SimpleCursorAdapter.java index 436b79b..7d3459e 100644 --- a/core/java/android/widget/SimpleCursorAdapter.java +++ b/core/java/android/widget/SimpleCursorAdapter.java @@ -20,9 +20,6 @@ import android.content.Context; import android.database.Cursor; import android.net.Uri; import android.view.View; -import android.view.ViewGroup; - -import java.util.WeakHashMap; /** * An easy adapter to map columns from a cursor to TextViews or ImageViews @@ -66,7 +63,6 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter { private CursorToStringConverter mCursorToStringConverter; private ViewBinder mViewBinder; private String[] mOriginalFrom; - private final WeakHashMap<View, View[]> mHolders = new WeakHashMap<View, View[]>(); /** * Constructor. @@ -91,29 +87,6 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter { findColumns(from); } - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return generateViewHolder(super.newView(context, cursor, parent)); - } - - @Override - public View newDropDownView(Context context, Cursor cursor, ViewGroup parent) { - return generateViewHolder(super.newDropDownView(context, cursor, parent)); - } - - private View generateViewHolder(View v) { - final int[] to = mTo; - final int count = to.length; - final View[] holder = new View[count]; - - for (int i = 0; i < count; i++) { - holder[i] = v.findViewById(to[i]); - } - mHolders.put(v, holder); - - return v; - } - /** * Binds all of the field names passed into the "to" parameter of the * constructor with their corresponding cursor columns as specified in the @@ -140,13 +113,13 @@ public class SimpleCursorAdapter extends ResourceCursorAdapter { */ @Override public void bindView(View view, Context context, Cursor cursor) { - final View[] holder = mHolders.get(view); final ViewBinder binder = mViewBinder; final int count = mTo.length; final int[] from = mFrom; + final int[] to = mTo; for (int i = 0; i < count; i++) { - final View v = holder[i]; + final View v = view.findViewById(to[i]); if (v != null) { boolean bound = false; if (binder != null) { diff --git a/core/java/android/widget/SlidingDrawer.java b/core/java/android/widget/SlidingDrawer.java index f706744..11d72de 100644 --- a/core/java/android/widget/SlidingDrawer.java +++ b/core/java/android/widget/SlidingDrawer.java @@ -43,7 +43,7 @@ import android.view.accessibility.AccessibilityEvent; * SlidingDrawer should be used as an overlay inside layouts. This means SlidingDrawer * should only be used inside of a FrameLayout or a RelativeLayout for instance. The * size of the SlidingDrawer defines how much space the content will occupy once slid - * out so SlidingDrawer should usually use fill_parent for both its dimensions. + * out so SlidingDrawer should usually use match_parent for both its dimensions. * * Inside an XML layout, SlidingDrawer must define the id of the handle and of the * content: @@ -51,8 +51,8 @@ import android.view.accessibility.AccessibilityEvent; * <pre class="prettyprint"> * <SlidingDrawer * android:id="@+id/drawer" - * android:layout_width="fill_parent" - * android:layout_height="fill_parent" + * android:layout_width="match_parent" + * android:layout_height="match_parent" * * android:handle="@+id/handle" * android:content="@+id/content"> @@ -64,8 +64,8 @@ import android.view.accessibility.AccessibilityEvent; * * <GridView * android:id="@id/content" - * android:layout_width="fill_parent" - * android:layout_height="fill_parent" /> + * android:layout_width="match_parent" + * android:layout_height="match_parent" /> * * </SlidingDrawer> * </pre> diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java index 412f817..78e2fee 100644 --- a/core/java/android/widget/TabHost.java +++ b/core/java/android/widget/TabHost.java @@ -327,8 +327,8 @@ mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1"); .addView( mCurrentView, new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.FILL_PARENT, - ViewGroup.LayoutParams.FILL_PARENT)); + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); } if (!mTabWidget.hasFocus()) { diff --git a/core/java/android/widget/TabWidget.java b/core/java/android/widget/TabWidget.java index c12d098..aa47e6d 100644 --- a/core/java/android/widget/TabWidget.java +++ b/core/java/android/widget/TabWidget.java @@ -16,8 +16,6 @@ package android.widget; -import com.android.internal.R; - import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; @@ -26,7 +24,6 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.view.View.OnFocusChangeListener; @@ -313,7 +310,7 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { if (child.getLayoutParams() == null) { final LinearLayout.LayoutParams lp = new LayoutParams( 0, - ViewGroup.LayoutParams.FILL_PARENT, 1.0f); + ViewGroup.LayoutParams.MATCH_PARENT, 1.0f); lp.setMargins(0, 0, 0, 0); child.setLayoutParams(lp); } @@ -328,7 +325,7 @@ public class TabWidget extends LinearLayout implements OnFocusChangeListener { ImageView divider = new ImageView(mContext); final LinearLayout.LayoutParams lp = new LayoutParams( mDividerDrawable.getIntrinsicWidth(), - LayoutParams.FILL_PARENT); + LayoutParams.MATCH_PARENT); lp.setMargins(0, 0, 0, 0); divider.setLayoutParams(lp); divider.setBackgroundDrawable(mDividerDrawable); diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java index afa2f3b..66500a3 100644 --- a/core/java/android/widget/TableLayout.java +++ b/core/java/android/widget/TableLayout.java @@ -52,7 +52,7 @@ import java.util.regex.Pattern; * {@link #setColumnCollapsed(int,boolean) setColumnCollapsed()}.</p> * * <p>The children of a TableLayout cannot specify the <code>layout_width</code> - * attribute. Width is always <code>FILL_PARENT</code>. However, the + * attribute. Width is always <code>MATCH_PARENT</code>. However, the * <code>layout_height</code> attribute can be defined by a child; default value * is {@link android.widget.TableLayout.LayoutParams#WRAP_CONTENT}. If the child * is a {@link android.widget.TableRow}, then the height is always @@ -621,7 +621,7 @@ public class TableLayout extends LinearLayout { /** * Returns a set of layout parameters with a width of - * {@link android.view.ViewGroup.LayoutParams#FILL_PARENT}, + * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}, * and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}. */ @Override @@ -647,7 +647,7 @@ public class TableLayout extends LinearLayout { /** * <p>This set of layout parameters enforces the width of each child to be - * {@link #FILL_PARENT} and the height of each child to be + * {@link #MATCH_PARENT} and the height of each child to be * {@link #WRAP_CONTENT}, but only if the height is not specified.</p> */ @SuppressWarnings({"UnusedDeclaration"}) @@ -663,14 +663,14 @@ public class TableLayout extends LinearLayout { * {@inheritDoc} */ public LayoutParams(int w, int h) { - super(FILL_PARENT, h); + super(MATCH_PARENT, h); } /** * {@inheritDoc} */ public LayoutParams(int w, int h, float initWeight) { - super(FILL_PARENT, h, initWeight); + super(MATCH_PARENT, h, initWeight); } /** @@ -679,7 +679,7 @@ public class TableLayout extends LinearLayout { * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.</p> */ public LayoutParams() { - super(FILL_PARENT, WRAP_CONTENT); + super(MATCH_PARENT, WRAP_CONTENT); } /** @@ -698,7 +698,7 @@ public class TableLayout extends LinearLayout { /** * <p>Fixes the row's width to - * {@link android.view.ViewGroup.LayoutParams#FILL_PARENT}; the row's + * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}; the row's * height is fixed to * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} if no layout * height is specified.</p> @@ -710,7 +710,7 @@ public class TableLayout extends LinearLayout { @Override protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { - this.width = FILL_PARENT; + this.width = MATCH_PARENT; if (a.hasValue(heightAttr)) { this.height = a.getLayoutDimension(heightAttr, "layout_height"); } else { diff --git a/core/java/android/widget/TableRow.java b/core/java/android/widget/TableRow.java index 5628cab..abf08bf 100644 --- a/core/java/android/widget/TableRow.java +++ b/core/java/android/widget/TableRow.java @@ -35,7 +35,7 @@ import android.view.ViewDebug; * <p>The children of a TableRow do not need to specify the * <code>layout_width</code> and <code>layout_height</code> attributes in the * XML file. TableRow always enforces those values to be respectively - * {@link android.widget.TableLayout.LayoutParams#FILL_PARENT} and + * {@link android.widget.TableLayout.LayoutParams#MATCH_PARENT} and * {@link android.widget.TableLayout.LayoutParams#WRAP_CONTENT}.</p> * * <p> @@ -299,7 +299,7 @@ public class TableRow extends LinearLayout { case LayoutParams.WRAP_CONTENT: spec = getChildMeasureSpec(widthMeasureSpec, 0, LayoutParams.WRAP_CONTENT); break; - case LayoutParams.FILL_PARENT: + case LayoutParams.MATCH_PARENT: spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); break; default: @@ -351,7 +351,7 @@ public class TableRow extends LinearLayout { /** * Returns a set of layout parameters with a width of - * {@link android.view.ViewGroup.LayoutParams#FILL_PARENT}, + * {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}, * a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT} and no spanning. */ @Override @@ -451,7 +451,7 @@ public class TableRow extends LinearLayout { * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.</p> */ public LayoutParams() { - super(FILL_PARENT, WRAP_CONTENT); + super(MATCH_PARENT, WRAP_CONTENT); column = -1; span = 1; } @@ -459,7 +459,7 @@ public class TableRow extends LinearLayout { /** * <p>Puts the view in the specified column.</p> * - * <p>Sets the child width to {@link android.view.ViewGroup.LayoutParams#FILL_PARENT} + * <p>Sets the child width to {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT} * and the child height to * {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}.</p> * @@ -490,7 +490,7 @@ public class TableRow extends LinearLayout { if (a.hasValue(widthAttr)) { width = a.getLayoutDimension(widthAttr, "layout_width"); } else { - width = FILL_PARENT; + width = MATCH_PARENT; } // We don't want to force users to specify a layout_height diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 201cc0c..12e8e29 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -4531,6 +4531,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Now use the delta to determine the actual amount of text // we need. partialEndOffset += delta; + if (partialStartOffset > N) { + partialStartOffset = N; + } else if (partialStartOffset < 0) { + partialStartOffset = 0; + } if (partialEndOffset > N) { partialEndOffset = N; } else if (partialEndOffset < 0) { @@ -4987,7 +4992,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int height = mLayoutParams.height; // If the size of the view does not depend on the size of the text, try to // start the marquee immediately - if (height != LayoutParams.WRAP_CONTENT && height != LayoutParams.FILL_PARENT) { + if (height != LayoutParams.WRAP_CONTENT && height != LayoutParams.MATCH_PARENT) { startMarquee(); } else { // Defer the start of the marquee until we know our width (see setFrame()) @@ -5302,7 +5307,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (desiredHeight != this.getHeight()) { sizeChanged = true; } - } else if (mLayoutParams.height == LayoutParams.FILL_PARENT) { + } else if (mLayoutParams.height == LayoutParams.MATCH_PARENT) { if (mDesiredHeightAtMeasure >= 0) { int desiredHeight = getDesiredHeight(); @@ -5349,7 +5354,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mEllipsize != TextUtils.TruncateAt.MARQUEE) { // In a fixed-height view, so use our new text layout. if (mLayoutParams.height != LayoutParams.WRAP_CONTENT && - mLayoutParams.height != LayoutParams.FILL_PARENT) { + mLayoutParams.height != LayoutParams.MATCH_PARENT) { invalidate(); return; } diff --git a/core/java/android/widget/ViewSwitcher.java b/core/java/android/widget/ViewSwitcher.java index 0dcaf95..71ae624 100644 --- a/core/java/android/widget/ViewSwitcher.java +++ b/core/java/android/widget/ViewSwitcher.java @@ -80,7 +80,7 @@ public class ViewSwitcher extends ViewAnimator { View child = mFactory.makeView(); LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (lp == null) { - lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); + lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); } addView(child, lp); return child; diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java index e55fbb8..bea009c 100644 --- a/core/java/android/widget/ZoomButtonsController.java +++ b/core/java/android/widget/ZoomButtonsController.java @@ -247,7 +247,7 @@ public class ZoomButtonsController implements View.OnTouchListener { LayoutParams.FLAG_LAYOUT_NO_LIMITS | LayoutParams.FLAG_ALT_FOCUSABLE_IM; lp.height = LayoutParams.WRAP_CONTENT; - lp.width = LayoutParams.FILL_PARENT; + lp.width = LayoutParams.MATCH_PARENT; lp.type = LayoutParams.TYPE_APPLICATION_PANEL; lp.format = PixelFormat.TRANSLUCENT; lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons; |
