diff options
Diffstat (limited to 'core/java')
32 files changed, 625 insertions, 554 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index ffcdcf7..2e66a4c 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -54,7 +54,6 @@ import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.media.AudioManager; import android.media.session.MediaController; -import android.media.session.MediaSession; import android.net.Uri; import android.os.Build; import android.os.Bundle; @@ -5641,8 +5640,8 @@ public class Activity extends ContextThemeWrapper if (info.taskAffinity == null) { return false; } - return !ActivityManagerNative.getDefault() - .targetTaskAffinityMatchesActivity(mToken, info.taskAffinity); + return ActivityManagerNative.getDefault() + .shouldUpRecreateTask(mToken, info.taskAffinity); } catch (RemoteException e) { return false; } catch (NameNotFoundException e) { diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 3dafa4b..5b81cc3 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -1997,11 +1997,11 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } - case TARGET_TASK_AFFINITY_MATCHES_ACTIVITY_TRANSACTION: { + case SHOULD_UP_RECREATE_TASK_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder token = data.readStrongBinder(); String destAffinity = data.readString(); - boolean res = targetTaskAffinityMatchesActivity(token, destAffinity); + boolean res = shouldUpRecreateTask(token, destAffinity); reply.writeNoException(); reply.writeInt(res ? 1 : 0); return true; @@ -4858,14 +4858,14 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); } - public boolean targetTaskAffinityMatchesActivity(IBinder token, String destAffinity) + public boolean shouldUpRecreateTask(IBinder token, String destAffinity) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeStrongBinder(token); data.writeString(destAffinity); - mRemote.transact(TARGET_TASK_AFFINITY_MATCHES_ACTIVITY_TRANSACTION, data, reply, 0); + mRemote.transact(SHOULD_UP_RECREATE_TASK_TRANSACTION, data, reply, 0); reply.readException(); boolean result = reply.readInt() != 0; data.recycle(); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 70514d8..1664408 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -393,7 +393,7 @@ public interface IActivityManager extends IInterface { public void keyguardWaitingForActivityDrawn() throws RemoteException; - public boolean targetTaskAffinityMatchesActivity(IBinder token, String destAffinity) + public boolean shouldUpRecreateTask(IBinder token, String destAffinity) throws RemoteException; public boolean navigateUpTo(IBinder token, Intent target, int resultCode, Intent resultData) @@ -702,7 +702,7 @@ public interface IActivityManager extends IInterface { int GET_MY_MEMORY_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+142; int KILL_PROCESSES_BELOW_FOREGROUND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+143; int GET_CURRENT_USER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+144; - int TARGET_TASK_AFFINITY_MATCHES_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+145; + int SHOULD_UP_RECREATE_TASK_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+145; int NAVIGATE_UP_TO_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+146; int SET_LOCK_SCREEN_SHOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+147; int FINISH_ACTIVITY_AFFINITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+148; diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 47967ba..f7300aa 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2579,20 +2579,35 @@ public class Notification implements Parcelable return bitmap; } + private boolean addProfileBadge(RemoteViews contentView, int resId) { + Bitmap profileBadge = getProfileBadge(); + + contentView.setViewVisibility(R.id.profile_badge_large_template, View.GONE); + contentView.setViewVisibility(R.id.profile_badge_line2, View.GONE); + contentView.setViewVisibility(R.id.profile_badge_line3, View.GONE); + + if (profileBadge != null) { + contentView.setImageViewBitmap(resId, profileBadge); + contentView.setViewVisibility(resId, View.VISIBLE); + + // Make sure Line 3 is visible. As badge will be here if there + // is no text to display. + if (resId == R.id.profile_badge_line3) { + contentView.setViewVisibility(R.id.line3, View.VISIBLE); + } + return true; + } + return false; + } + private RemoteViews applyStandardTemplate(int resId) { - Bitmap profileIcon = getProfileBadge(); RemoteViews contentView = new BuilderRemoteViews(mContext.getPackageName(), mOriginatingUserId, resId); boolean showLine3 = false; boolean showLine2 = false; + boolean contentTextInLine2 = false; - if (profileIcon != null) { - contentView.setImageViewBitmap(R.id.profile_icon, profileIcon); - contentView.setViewVisibility(R.id.profile_icon, View.VISIBLE); - } else { - contentView.setViewVisibility(R.id.profile_icon, View.GONE); - } if (mLargeIcon != null) { contentView.setImageViewBitmap(R.id.icon, mLargeIcon); processLargeIcon(mLargeIcon, contentView); @@ -2639,6 +2654,7 @@ public class Notification implements Parcelable contentView.setTextViewText(R.id.text2, processLegacyText(mContentText)); contentView.setViewVisibility(R.id.text2, View.VISIBLE); showLine2 = true; + contentTextInLine2 = true; } else { contentView.setViewVisibility(R.id.text2, View.GONE); } @@ -2681,6 +2697,15 @@ public class Notification implements Parcelable hasThreeLines(), mContext.getResources().getConfiguration().fontScale), 0, 0); + // We want to add badge to first line of text. + boolean addedBadge = addProfileBadge(contentView, + contentTextInLine2 ? R.id.profile_badge_line2 : R.id.profile_badge_line3); + // If we added the badge to line 3 then we should show line 3. + if (addedBadge && !contentTextInLine2) { + showLine3 = true; + } + + // Note getStandardView may hide line 3 again. contentView.setViewVisibility(R.id.line3, showLine3 ? View.VISIBLE : View.GONE); contentView.setViewVisibility(R.id.overflow_divider, showLine3 ? View.VISIBLE : View.GONE); return contentView; @@ -3347,6 +3372,8 @@ public class Notification implements Parcelable contentView.setViewVisibility(R.id.overflow_divider, View.VISIBLE); contentView.setViewVisibility(R.id.line3, View.VISIBLE); } else { + // Clear text in case we use the line to show the profile badge. + contentView.setTextViewText(R.id.text, ""); contentView.setViewVisibility(R.id.overflow_divider, View.GONE); contentView.setViewVisibility(R.id.line3, View.GONE); } @@ -3507,6 +3534,9 @@ public class Notification implements Parcelable applyTopPadding(contentView); + boolean twoTextLines = mBuilder.mSubText != null && mBuilder.mContentText != null; + mBuilder.addProfileBadge(contentView, + twoTextLines ? R.id.profile_badge_line2 : R.id.profile_badge_line3); return contentView; } @@ -3632,6 +3662,8 @@ public class Notification implements Parcelable applyTopPadding(contentView); + mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template); + return contentView; } @@ -3769,6 +3801,8 @@ public class Notification implements Parcelable applyTopPadding(contentView); + mBuilder.addProfileBadge(contentView, R.id.profile_badge_large_template); + return contentView; } diff --git a/core/java/android/app/job/JobInfo.java b/core/java/android/app/job/JobInfo.java index b7af544..e4ed470e 100644 --- a/core/java/android/app/job/JobInfo.java +++ b/core/java/android/app/job/JobInfo.java @@ -26,6 +26,9 @@ import android.os.PersistableBundle; * Container of data passed to the {@link android.app.job.JobScheduler} fully encapsulating the * parameters required to schedule work against the calling application. These are constructed * using the {@link JobInfo.Builder}. + * You must specify at least one sort of constraint on the JobInfo object that you are creating. + * The goal here is to provide the scheduler with high-level semantics about the work you want to + * accomplish. Doing otherwise with throw an exception in your app. */ public class JobInfo implements Parcelable { public interface NetworkType { @@ -434,7 +437,7 @@ public class JobInfo implements Parcelable { * @return The job object to hand to the JobScheduler. This object is immutable. */ public JobInfo build() { - // Allow tasks with no constraints. What am I, a database? + // Allow jobs with no constraints - What am I, a database? if (!mHasEarlyConstraint && !mHasLateConstraint && !mRequiresCharging && !mRequiresDeviceIdle && mNetworkCapabilities == NetworkType.NONE) { throw new IllegalArgumentException("You're trying to build a job with no " + diff --git a/core/java/android/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java index f95ed0f..aba90e4 100644 --- a/core/java/android/hardware/hdmi/HdmiClient.java +++ b/core/java/android/hardware/hdmi/HdmiClient.java @@ -50,7 +50,7 @@ public abstract class HdmiClient { try { mService.sendKeyEvent(getDeviceType(), keyCode, isPressed); } catch (RemoteException e) { - Log.e(TAG, "queryDisplayStatus threw exception ", e); + Log.e(TAG, "sendKeyEvent threw exception ", e); } } diff --git a/core/java/android/hardware/hdmi/HdmiTvClient.java b/core/java/android/hardware/hdmi/HdmiTvClient.java index 354c05e..c37fb5b 100644 --- a/core/java/android/hardware/hdmi/HdmiTvClient.java +++ b/core/java/android/hardware/hdmi/HdmiTvClient.java @@ -35,6 +35,11 @@ import libcore.util.EmptyArray; public final class HdmiTvClient extends HdmiClient { private static final String TAG = "HdmiTvClient"; + /** + * Size of MHL scratchpad register. + */ + public static final int SCRATCHPAD_DATA_SIZE = 16; + HdmiTvClient(IHdmiControlService service) { super(service); } @@ -80,6 +85,15 @@ public final class HdmiTvClient extends HdmiClient { } } + private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) { + return new IHdmiControlCallback.Stub() { + @Override + public void onComplete(int result) { + callback.onComplete(result); + } + }; + } + /** * Select a HDMI port to be a new route path. * @@ -126,6 +140,15 @@ public final class HdmiTvClient extends HdmiClient { } } + private static IHdmiInputChangeListener getListenerWrapper(final InputChangeListener listener) { + return new IHdmiInputChangeListener.Stub() { + @Override + public void onChanged(HdmiDeviceInfo info) { + listener.onChanged(info); + } + }; + } + /** * Set system audio volume * @@ -170,6 +193,38 @@ public final class HdmiTvClient extends HdmiClient { } } + private static IHdmiRecordListener getListenerWrapper(final HdmiRecordListener callback) { + return new IHdmiRecordListener.Stub() { + @Override + public byte[] getOneTouchRecordSource(int recorderAddress) { + HdmiRecordSources.RecordSource source = + callback.getOneTouchRecordSource(recorderAddress); + if (source == null) { + return EmptyArray.BYTE; + } + byte[] data = new byte[source.getDataSize(true)]; + source.toByteArray(true, data, 0); + return data; + } + + @Override + public void onOneTouchRecordResult(int result) { + callback.onOneTouchRecordResult(result); + } + + @Override + public void onTimerRecordingResult(int result) { + callback.onTimerRecordingResult( + HdmiRecordListener.TimerStatusData.parseFrom(result)); + } + + @Override + public void onClearTimerRecordingResult(int result) { + callback.onClearTimerRecordingResult(result); + } + }; + } + /** * Start one touch recording with the given recorder address and recorder source. * <p> @@ -276,53 +331,63 @@ public final class HdmiTvClient extends HdmiClient { } } - private static IHdmiControlCallback getCallbackWrapper(final SelectCallback callback) { - return new IHdmiControlCallback.Stub() { - @Override - public void onComplete(int result) { - callback.onComplete(result); - } - }; + /** + * Interface used to get incoming MHL scratchpad command. + */ + public interface HdmiMhlScratchpadCommandListener { + void onReceived(int portId, int offset, int length, byte[] data); } - private static IHdmiInputChangeListener getListenerWrapper(final InputChangeListener listener) { - return new IHdmiInputChangeListener.Stub() { - @Override - public void onChanged(HdmiDeviceInfo info) { - listener.onChanged(info); - } - }; + /** + * Set {@link HdmiMhlScratchpadCommandListener} to get incoming MHL sSratchpad command. + * + * @param listener to receive incoming MHL Scratchpad command + */ + public void setHdmiMhlScratchpadCommandListener(HdmiMhlScratchpadCommandListener listener) { + if (listener == null) { + throw new IllegalArgumentException("listener must not be null."); + } + try { + mService.addHdmiMhlScratchpadCommandListener(getListenerWrapper(listener)); + } catch (RemoteException e) { + Log.e(TAG, "failed to set hdmi mhl scratchpad command listener: ", e); + } } - private static IHdmiRecordListener getListenerWrapper(final HdmiRecordListener callback) { - return new IHdmiRecordListener.Stub() { + private IHdmiMhlScratchpadCommandListener getListenerWrapper( + final HdmiMhlScratchpadCommandListener listener) { + return new IHdmiMhlScratchpadCommandListener.Stub() { @Override - public byte[] getOneTouchRecordSource(int recorderAddress) { - HdmiRecordSources.RecordSource source = - callback.getOneTouchRecordSource(recorderAddress); - if (source == null) { - return EmptyArray.BYTE; - } - byte[] data = new byte[source.getDataSize(true)]; - source.toByteArray(true, data, 0); - return data; - } - - @Override - public void onOneTouchRecordResult(int result) { - callback.onOneTouchRecordResult(result); + public void onReceived(int portId, int offset, int length, byte[] data) { + listener.onReceived(portId, offset, length, data); } + }; + } - @Override - public void onTimerRecordingResult(int result) { - callback.onTimerRecordingResult( - HdmiRecordListener.TimerStatusData.parseFrom(result)); - } + /** + * Send MHL Scratchpad command to the device connected to a port of the given portId. + * + * @param portId id of port to send MHL Scratchpad command + * @param offset offset in the in given data + * @param length length of data. offset + length should be bound to length of data. + * @param data container for Scratchpad data. It should be 16 bytes. + * @throws IllegalArgumentException if the given parameters are invalid + */ + public void sendScratchpadCommand(int portId, int offset, int length, byte[] data) { + if (data == null || data.length != SCRATCHPAD_DATA_SIZE) { + throw new IllegalArgumentException("Invalid scratchpad data."); + } + if (offset < 0 || offset >= SCRATCHPAD_DATA_SIZE) { + throw new IllegalArgumentException("Invalid offset:" + offset); + } + if (length < 0 || offset + length > SCRATCHPAD_DATA_SIZE) { + throw new IllegalArgumentException("Invalid length:" + length); + } - @Override - public void onClearTimerRecordingResult(int result) { - callback.onClearTimerRecordingResult(result); - } - }; + try { + mService.sendScratchpadCommand(portId, offset, length, data); + } catch (RemoteException e) { + Log.e(TAG, "failed to send scratchpad command: ", e); + } } } diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl index 17f290b..3bd45ed 100644 --- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl +++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl @@ -22,6 +22,7 @@ import android.hardware.hdmi.IHdmiControlCallback; import android.hardware.hdmi.IHdmiDeviceEventListener; import android.hardware.hdmi.IHdmiHotplugEventListener; import android.hardware.hdmi.IHdmiInputChangeListener; +import android.hardware.hdmi.IHdmiMhlScratchpadCommandListener; import android.hardware.hdmi.IHdmiRecordListener; import android.hardware.hdmi.IHdmiSystemAudioModeChangeListener; import android.hardware.hdmi.IHdmiVendorCommandListener; @@ -66,4 +67,6 @@ interface IHdmiControlService { void stopOneTouchRecord(int recorderAddress); void startTimerRecording(int recorderAddress, int sourceType, in byte[] recordSource); void clearTimerRecording(int recorderAddress, int sourceType, in byte[] recordSource); + void sendScratchpadCommand(int portId, int offset, int length, in byte[] data); + void addHdmiMhlScratchpadCommandListener(IHdmiMhlScratchpadCommandListener listener); } diff --git a/core/java/android/view/inputmethod/CursorAnchorInfoRequest.aidl b/core/java/android/hardware/hdmi/IHdmiMhlScratchpadCommandListener.aidl index 41ef7cc6..4176597 100644 --- a/core/java/android/view/inputmethod/CursorAnchorInfoRequest.aidl +++ b/core/java/android/hardware/hdmi/IHdmiMhlScratchpadCommandListener.aidl @@ -14,6 +14,14 @@ * limitations under the License. */ -package android.view.inputmethod; +package android.hardware.hdmi; -parcelable CursorAnchorInfoRequest; + /** + * Callback interface definition for MHL client to get the scratchpad + * command. + * + * @hide + */ + oneway interface IHdmiMhlScratchpadCommandListener { + void onReceived(int portId, int offset, int length, in byte[] data); + } diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index ba811b7..2eb42a7 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -53,7 +53,6 @@ import android.view.WindowManager.BadTokenException; import android.view.animation.AnimationUtils; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CursorAnchorInfo; -import android.view.inputmethod.CursorAnchorInfoRequest; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -1711,13 +1710,12 @@ public class InputMethodService extends AbstractInputMethodService { } /** - * Called when the application has reported a new location of its text cursor. This is only - * called if explicitly requested by the input method. The default implementation does nothing. - * @param newCursor The new cursor position, in screen coordinates if the input method calls - * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} with - * {@link CursorAnchorInfoRequest#FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES}. Otherwise, - * this is in local coordinates. + * Called when the application has reported a new location of its text + * cursor. This is only called if explicitly requested by the input method. + * The default implementation does nothing. + * @deprecated Use {#link onUpdateCursorAnchorInfo(CursorAnchorInfo)} instead. */ + @Deprecated public void onUpdateCursor(Rect newCursor) { // Intentionally empty } diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java index 0de3f26..d2a4728 100644 --- a/core/java/android/net/Network.java +++ b/core/java/android/net/Network.java @@ -16,10 +16,10 @@ package android.net; -import android.net.NetworkBoundURLFactory; import android.net.NetworkUtils; import android.os.Parcelable; import android.os.Parcel; +import android.system.ErrnoException; import java.io.IOException; import java.net.InetAddress; @@ -30,6 +30,7 @@ import java.net.SocketAddress; import java.net.SocketException; import java.net.UnknownHostException; import java.net.URL; +import java.net.URLConnection; import java.net.URLStreamHandler; import java.util.concurrent.atomic.AtomicReference; import javax.net.SocketFactory; @@ -54,7 +55,7 @@ public class Network implements Parcelable { public final int netId; // Objects used to perform per-network operations such as getSocketFactory - // and getBoundURL, and a lock to protect access to them. + // and openConnection, and a lock to protect access to them. private volatile NetworkBoundSocketFactory mNetworkBoundSocketFactory = null; private volatile OkHttpClient mOkHttpClient = null; private Object mLock = new Object(); @@ -157,12 +158,7 @@ public class Network implements Parcelable { @Override public Socket createSocket() throws IOException { Socket socket = new Socket(); - // Query a property of the underlying socket to ensure the underlying - // socket exists so a file descriptor is available to bind to a network. - socket.getReuseAddress(); - if (!NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), mNetId)) { - throw new SocketException("Failed to bind socket to network."); - } + bindSocket(socket); return socket; } } @@ -187,73 +183,63 @@ public class Network implements Parcelable { return mNetworkBoundSocketFactory; } - /** The default NetworkBoundURLFactory, used if setNetworkBoundURLFactory is never called. */ - private static final NetworkBoundURLFactory DEFAULT_URL_FACTORY = new NetworkBoundURLFactory() { - public URL getBoundURL(final Network network, URL url) throws MalformedURLException { - if (network.mOkHttpClient == null) { - synchronized (network.mLock) { - if (network.mOkHttpClient == null) { - HostResolver hostResolver = new HostResolver() { - @Override - public InetAddress[] getAllByName(String host) - throws UnknownHostException { - return network.getAllByName(host); - } - }; - network.mOkHttpClient = new OkHttpClient() - .setSocketFactory(network.getSocketFactory()) - .setHostResolver(hostResolver); - } + private void maybeInitHttpClient() { + if (mOkHttpClient == null) { + synchronized (mLock) { + if (mOkHttpClient == null) { + HostResolver hostResolver = new HostResolver() { + @Override + public InetAddress[] getAllByName(String host) throws UnknownHostException { + return Network.this.getAllByName(host); + } + }; + mOkHttpClient = new OkHttpClient() + .setSocketFactory(getSocketFactory()) + .setHostResolver(hostResolver); } } - - String protocol = url.getProtocol(); - URLStreamHandler handler = network.mOkHttpClient.createURLStreamHandler(protocol); - if (handler == null) { - // OkHttpClient only supports HTTP and HTTPS and returns a null URLStreamHandler if - // passed another protocol. - throw new MalformedURLException("Invalid URL or unrecognized protocol " + protocol); - } - return new URL(url, "", handler); } - }; - - private static AtomicReference<NetworkBoundURLFactory> sNetworkBoundURLFactory = - new AtomicReference <NetworkBoundURLFactory>(DEFAULT_URL_FACTORY); + } /** - * Returns a {@link URL} based on the given URL but bound to this {@code Network}, - * such that opening the URL will send all network traffic on this Network. + * Opens the specified {@link URL} on this {@code Network}, such that all traffic will be sent + * on this Network. The URL protocol must be {@code HTTP} or {@code HTTPS}. * - * Note that if this {@code Network} ever disconnects, any URL object generated by this method - * in the past or future will cease to work. - * - * The returned URL may have a {@link URLStreamHandler} explicitly set, which may not be the - * handler generated by the factory set with {@link java.net.URL#setURLStreamHandlerFactory}. To - * affect the {@code URLStreamHandler}s of URLs returned by this method, call - * {@link #setNetworkBoundURLFactory}. - * - * Because the returned URLs may have an explicit {@code URLStreamHandler} set, using them as a - * context when constructing other URLs and explicitly specifying a {@code URLStreamHandler} may - * result in URLs that are no longer bound to the same {@code Network}. - * - * The default implementation only supports {@code HTTP} and {@code HTTPS} URLs. - * - * @return a {@link URL} bound to this {@code Network}. + * @return a {@code URLConnection} to the resource referred to by this URL. + * @throws MalformedURLException if the URL protocol is not HTTP or HTTPS. + * @throws IOException if an error occurs while opening the connection. + * @see java.net.URL#openConnection() */ - public URL getBoundURL(URL url) throws MalformedURLException { - return sNetworkBoundURLFactory.get().getBoundURL(this, url); + public URLConnection openConnection(URL url) throws IOException { + maybeInitHttpClient(); + String protocol = url.getProtocol(); + URLStreamHandler handler = mOkHttpClient.createURLStreamHandler(protocol); + if (handler == null) { + // OkHttpClient only supports HTTP and HTTPS and returns a null URLStreamHandler if + // passed another protocol. + throw new MalformedURLException("Invalid URL or unrecognized protocol " + protocol); + } + return new URL(url, "", handler).openConnection(); } /** - * Sets the {@link NetworkBoundURLFactory} to be used by future {@link #getBoundURL} calls. - * If {@code null}, clears any factory that was previously specified. + * Binds the specified {@link Socket} to this {@code Network}. All data traffic on the socket + * will be sent on this {@code Network}, irrespective of any process-wide network binding set by + * {@link ConnectivityManager#setProcessDefaultNetwork}. The socket must not be connected. */ - public static void setNetworkBoundURLFactory(NetworkBoundURLFactory factory) { - if (factory == null) { - factory = DEFAULT_URL_FACTORY; + public void bindSocket(Socket socket) throws IOException { + if (socket.isConnected()) { + throw new SocketException("Socket is connected"); + } + // Query a property of the underlying socket to ensure the underlying + // socket exists so a file descriptor is available to bind to a network. + socket.getReuseAddress(); + int err = NetworkUtils.bindSocketToNetwork(socket.getFileDescriptor$().getInt$(), netId); + if (err != 0) { + // bindSocketToNetwork returns negative errno. + throw new ErrnoException("Binding socket to network " + netId, -err) + .rethrowAsSocketException(); } - sNetworkBoundURLFactory.set(factory); } // implement the Parcelable interface diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 54d8676..d2a2997 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -128,8 +128,9 @@ public class NetworkUtils { /** * Explicitly binds {@code socketfd} to the network designated by {@code netId}. This * overrides any binding via {@link #bindProcessToNetwork}. + * @return 0 on success or negative errno on failure. */ - public native static boolean bindSocketToNetwork(int socketfd, int netId); + public native static int bindSocketToNetwork(int socketfd, int netId); /** * Protect {@code socketfd} from VPN connections. After protecting, data sent through diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index 8db99a5..942da5a 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -24,13 +24,17 @@ import android.content.Context; import android.content.Intent; import android.content.pm.UserInfo; import android.database.Cursor; +import android.location.Country; +import android.location.CountryDetector; import android.net.Uri; import android.os.UserHandle; import android.os.UserManager; import android.provider.ContactsContract.CommonDataKinds.Callable; import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.Data; import android.provider.ContactsContract.DataUsageFeedback; import android.telecomm.PhoneAccountHandle; +import android.telephony.PhoneNumberUtils; import android.text.TextUtils; import com.android.internal.telephony.CallerInfo; @@ -345,6 +349,13 @@ public class CallLog { public static final String PHONE_ACCOUNT_ID = "subscription_id"; /** + * If a successful call is made that is longer than this duration, update the phone number + * in the ContactsProvider with the normalized version of the number, based on the user's + * current country code. + */ + private static final int MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS = 1000 * 10; + + /** * Adds a call to the call log. * * @param ci the CallerInfo object to get the target contact from. Can be null @@ -484,12 +495,13 @@ public class CallLog { if (cursor != null) { try { if (cursor.getCount() > 0 && cursor.moveToFirst()) { - final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() - .appendPath(cursor.getString(0)) - .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, - DataUsageFeedback.USAGE_TYPE_CALL) - .build(); - resolver.update(feedbackUri, new ContentValues(), null, null); + final String dataId = cursor.getString(0); + updateDataUsageStatForData(resolver, dataId); + if (duration >= MIN_DURATION_FOR_NORMALIZED_NUMBER_UPDATE_MS + && callType == Calls.OUTGOING_TYPE + && TextUtils.isEmpty(ci.normalizedNumber)) { + updateNormalizedNumber(context, resolver, dataId, number); + } } } finally { cursor.close(); @@ -562,5 +574,53 @@ public class CallLog { + " LIMIT -1 OFFSET 500)", null); return result; } + + private static void updateDataUsageStatForData(ContentResolver resolver, String dataId) { + final Uri feedbackUri = DataUsageFeedback.FEEDBACK_URI.buildUpon() + .appendPath(dataId) + .appendQueryParameter(DataUsageFeedback.USAGE_TYPE, + DataUsageFeedback.USAGE_TYPE_CALL) + .build(); + resolver.update(feedbackUri, new ContentValues(), null, null); + } + + /** + * Update the normalized phone number for the given dataId in the ContactsProvider, based + * on the user's current country. + */ + private static void updateNormalizedNumber(Context context, ContentResolver resolver, + String dataId, String number) { + if (TextUtils.isEmpty(number) || TextUtils.isEmpty(dataId)) { + return; + } + + final String countryIso = getCurrentCountryIso(context); + if (TextUtils.isEmpty(countryIso)) { + return; + } + + final String normalizedNumber = PhoneNumberUtils.formatNumberToE164(number, + getCurrentCountryIso(context)); + if (TextUtils.isEmpty(normalizedNumber)) { + return; + } + + final ContentValues values = new ContentValues(); + values.put(Phone.NORMALIZED_NUMBER, normalizedNumber); + resolver.update(Data.CONTENT_URI, values, Data._ID + "=?", new String[] {dataId}); + } + + private static String getCurrentCountryIso(Context context) { + String countryIso = null; + final CountryDetector detector = (CountryDetector) context.getSystemService( + Context.COUNTRY_DETECTOR); + if (detector != null) { + final Country country = detector.detectCountry(); + if (country != null) { + countryIso = country.getCountryIso(); + } + } + return countryIso; + } } } diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 681717c..964b054 100644 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -657,11 +657,15 @@ public class KeyEvent extends InputEvent implements Parcelable { /** Key code constant: TV data service key. * Displays data services like weather, sports. */ public static final int KEYCODE_TV_DATA_SERVICE = 230; + /** Key code constant: Voice Assist key. + * Launches the global voice assist activity. Not delivered to applications. */ + public static final int KEYCODE_VOICE_ASSIST = 231; - private static final int LAST_KEYCODE = KEYCODE_TV_DATA_SERVICE; + private static final int LAST_KEYCODE = KEYCODE_VOICE_ASSIST; // NOTE: If you add a new keycode here you must also add it to: // isSystem() + // isWakeKey() // frameworks/native/include/android/keycodes.h // frameworks/native/include/input/InputEventLabels.h // frameworks/base/core/res/res/values/attrs.xml diff --git a/core/java/android/view/ViewAnimationUtils.java b/core/java/android/view/ViewAnimationUtils.java index eeff90a..7ced088 100644 --- a/core/java/android/view/ViewAnimationUtils.java +++ b/core/java/android/view/ViewAnimationUtils.java @@ -26,7 +26,7 @@ import android.animation.RevealAnimator; public final class ViewAnimationUtils { private ViewAnimationUtils() {} /** - * Returns a ValueAnimator which can animate a clipping circle. + * Returns an Animator which can animate a clipping circle. * * Any shadow cast by the View will respect the circular clip from this animator. * diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index 8cae27b..4d2f57a 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -429,25 +429,10 @@ public class BaseInputConnection implements InputConnection { } /** - * The default implementation is responsible for handling - * {@link CursorAnchorInfoRequest#TYPE_CURSOR_RECT}. In fact, for derived classes, calling - * {@code super.requestCursorAnchorInfo(request)} is the only way to handle - * {@link CursorAnchorInfoRequest#TYPE_CURSOR_RECT}. + * The default implementation does nothing. */ - public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) { - // This implementation supports TYPE_CURSOR_RECT only. - if (request == null || - request.getRequestType() != CursorAnchorInfoRequest.TYPE_CURSOR_RECT) { - return CursorAnchorInfoRequest.RESULT_NOT_HANDLED; - } - if (mIMM == null) { - // In this case, TYPE_CURSOR_RECT is not handled. - // TODO: Return some notification code for the input method that indicates - // Cursor rect information is temporarily unavailable. - return CursorAnchorInfoRequest.RESULT_NOT_HANDLED; - } - mIMM.setCursorRectMonitorMode(request.getRequestFlags()); - return CursorAnchorInfoRequest.RESULT_SCHEDULED; + public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { + return false; } /** diff --git a/core/java/android/view/inputmethod/CursorAnchorInfoRequest.java b/core/java/android/view/inputmethod/CursorAnchorInfoRequest.java deleted file mode 100644 index e4c94f2..0000000 --- a/core/java/android/view/inputmethod/CursorAnchorInfoRequest.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package android.view.inputmethod; - -import android.inputmethodservice.InputMethodService; -import android.os.Parcel; -import android.os.Parcelable; -import android.view.View; - -/** - * Used to enable or disable event notification for - * {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}. This class is also used to - * enable {@link InputMethodService#onUpdateCursor(android.graphics.Rect)} for existing editors - * that have not supported {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}. - */ -public final class CursorAnchorInfoRequest implements Parcelable { - private final int mRequestType; - private final int mRequestFlags; - - /** - * Not handled by the editor. - */ - public static final int RESULT_NOT_HANDLED = 0x00; - /** - * Request is scheduled in the editor task queue. - */ - public static final int RESULT_SCHEDULED = 0x01; - - /** - * The request is for {@link InputMethodService#onUpdateCursorAnchorInfo(CursorAnchorInfo)}. - * This mechanism is powerful enough to retrieve fine-grained positional information of - * characters in the editor. - */ - public static final int TYPE_CURSOR_ANCHOR_INFO = 0x01; - /** - * The editor is requested to call - * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} - * whenever cursor/anchor position is changed. To disable monitoring, call - * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} again with - * {@link #TYPE_CURSOR_ANCHOR_INFO} and this flag off. - * <p> - * This flag can be used together with {@link #FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE}. - * </p> - */ - public static final int FLAG_CURSOR_ANCHOR_INFO_MONITOR = 0x01; - /** - * The editor is requested to call - * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} at - * once, as soon as possible, regardless of cursor/anchor position changes. This flag can be - * used together with {@link #FLAG_CURSOR_ANCHOR_INFO_MONITOR}. - */ - public static final int FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE = 0x02; - - /** - * The request is for {@link InputMethodService#onUpdateCursor(android.graphics.Rect)}. This - * mechanism has been available since API Level 3 (CUPCAKE) but only the cursor rectangle can - * be retrieved with this mechanism. - */ - public static final int TYPE_CURSOR_RECT = 0x02; - /** - * The editor is requested to call - * {@link InputMethodManager#updateCursor(android.view.View, int, int, int, int)} - * whenever the cursor position is changed. To disable monitoring, call - * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} again with - * {@link #TYPE_CURSOR_RECT} and this flag off. - * <p> - * This flag can be used together with {@link #FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES}. - * </p> - */ - public static final int FLAG_CURSOR_RECT_MONITOR = 0x01; - /** - * {@link InputMethodManager#updateCursor(android.view.View, int, int, int, int)} should be - * called back in screen coordinates. To receive cursor position in local coordinates, call - * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} again with - * {@link #TYPE_CURSOR_RECT} and this flag off. - */ - public static final int FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES = 0x02; - /** - * {@link InputMethodManager#updateCursor(android.view.View, int, int, int, int)} should be - * called back in screen coordinates after coordinate conversion with {@link View#getMatrix()}. - * To disable coordinate conversion with {@link View#getMatrix()} again, call - * {@link InputConnection#requestCursorAnchorInfo(CursorAnchorInfoRequest)} with - * {@link #TYPE_CURSOR_RECT} and this flag off. - * - * <p> - * The flag is ignored if {@link #FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES} is off. - * </p> - */ - public static final int FLAG_CURSOR_RECT_WITH_VIEW_MATRIX = 0x04; - - /** - * Constructs the object with request type and type-specific flags. - * - * @param requestType the type of this request. Currently {@link #TYPE_CURSOR_ANCHOR_INFO} or - * {@link #TYPE_CURSOR_RECT} is supported. - * @param requestFlags the flags for the given request type. - */ - public CursorAnchorInfoRequest(int requestType, int requestFlags) { - mRequestType = requestType; - mRequestFlags = requestFlags; - } - - /** - * Used to make this class parcelable. - * - * @param source the parcel from which the object is unmarshalled. - */ - public CursorAnchorInfoRequest(Parcel source) { - mRequestType = source.readInt(); - mRequestFlags = source.readInt(); - } - - /** - * @return the type of this request. - */ - public int getRequestType() { - return mRequestType; - } - - /** - * @return the flags that are specific to the type of this request. - */ - public int getRequestFlags() { - return mRequestFlags; - } - - /** - * Used to package this object into a {@link Parcel}. - * - * @param dest The {@link Parcel} to be written. - * @param flags The flags used for parceling. - */ - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mRequestType); - dest.writeInt(mRequestFlags); - } - - @Override - public int hashCode(){ - return mRequestType * 31 + mRequestFlags; - } - - @Override - public boolean equals(Object obj){ - if (obj == null) { - return false; - } - if (this == obj) { - return true; - } - if (!(obj instanceof CursorAnchorInfoRequest)) { - return false; - } - final CursorAnchorInfoRequest that = (CursorAnchorInfoRequest) obj; - if (hashCode() != that.hashCode()) { - return false; - } - return mRequestType != that.mRequestType && mRequestFlags == that.mRequestFlags; - } - - @Override - public String toString() { - return "CursorAnchorInfoRequest{mRequestType=" + mRequestType - + " mRequestFlags=" + mRequestFlags - + "}"; - } - - /** - * Used to make this class parcelable. - */ - public static final Parcelable.Creator<CursorAnchorInfoRequest> CREATOR = - new Parcelable.Creator<CursorAnchorInfoRequest>() { - @Override - public CursorAnchorInfoRequest createFromParcel(Parcel source) { - return new CursorAnchorInfoRequest(source); - } - - @Override - public CursorAnchorInfoRequest[] newArray(int size) { - return new CursorAnchorInfoRequest[size]; - } - }; - - @Override - public int describeContents() { - return 0; - } -} diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java index dff91dc..ca094c1 100644 --- a/core/java/android/view/inputmethod/InputConnection.java +++ b/core/java/android/view/inputmethod/InputConnection.java @@ -725,13 +725,34 @@ public interface InputConnection { public boolean performPrivateCommand(String action, Bundle data); /** - * Called by the IME to ask the editor for calling back + * The editor is requested to call + * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} at + * once, as soon as possible, regardless of cursor/anchor position changes. This flag can be + * used together with {@link #REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR}. + */ + public static final int REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE = 1 << 0; + + /** + * The editor is requested to call + * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} + * whenever cursor/anchor position is changed. To disable monitoring, call + * {@link InputConnection#requestUpdateCursorAnchorInfo(int)} again with this flag off. + * <p> + * This flag can be used together with {@link #REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE}. + * </p> + */ + public static final int REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR = 1 << 1; + + /** + * Called by the input method to ask the editor for calling back * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)} to * notify cursor/anchor locations. * - * @param request the details of the request. - * @return a result code that depends on {@link CursorAnchorInfoRequest#getRequestType()}. See - * {@link CursorAnchorInfoRequest} for details. + * @param cursorUpdateMode {@link #REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE} and/or + * {@link #REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR} + * @return {@code true} if the request is scheduled. {@code false} to indicate that when the + * application will not call + * {@link InputMethodManager#updateCursorAnchorInfo(android.view.View, CursorAnchorInfo)}. */ - public int requestCursorAnchorInfo(CursorAnchorInfoRequest request); + public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode); } diff --git a/core/java/android/view/inputmethod/InputConnectionWrapper.java b/core/java/android/view/inputmethod/InputConnectionWrapper.java index c831d7c..d95df25 100644 --- a/core/java/android/view/inputmethod/InputConnectionWrapper.java +++ b/core/java/android/view/inputmethod/InputConnectionWrapper.java @@ -126,7 +126,7 @@ public class InputConnectionWrapper implements InputConnection { return mTarget.performPrivateCommand(action, data); } - public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) { - return mTarget.requestCursorAnchorInfo(request); + public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { + return mTarget.requestUpdateCursorAnchorInfo(cursorUpdateMode); } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index eec3570..0a472c7 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -313,9 +313,8 @@ public final class InputMethodManager { CompletionInfo[] mCompletions; // Cursor position on the screen. - Rect mNextCursorRect = new Rect(); + Rect mTmpCursorRect = new Rect(); Rect mCursorRect = new Rect(); - RectF mTempRectF = new RectF(); int mCursorSelStart; int mCursorSelEnd; int mCursorCandStart; @@ -372,28 +371,12 @@ public final class InputMethodManager { InputChannel mCurChannel; ImeInputEventSender mCurSender; - private static final int CURSOR_RECT_MONITOR_MODE_NONE = 0x0; - - private static final int CURSOR_RECT_MONITOR_FLAG_MASK = - CursorAnchorInfoRequest.FLAG_CURSOR_RECT_MONITOR | - CursorAnchorInfoRequest.FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES | - CursorAnchorInfoRequest.FLAG_CURSOR_RECT_WITH_VIEW_MATRIX; - - private static final int CURSOR_ANCHOR_INFO_MONITOR_MODE_NONE = 0x0; - - private static final int CURSOR_ANCHOR_INFO_MONITOR_FLAG_MASK = - CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_MONITOR | - CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE; - - /** - * The monitor mode for {@link #updateCursor(View, int, int, int, int)}. - */ - private int mCursorRectMonitorMode = CURSOR_RECT_MONITOR_MODE_NONE; + private static final int REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE = 0x0; /** * The monitor mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}. */ - private int mCursorAnchorInfoMonitorMode = CURSOR_ANCHOR_INFO_MONITOR_MODE_NONE; + private int mRequestUpdateCursorAnchorInfoMonitorMode = REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE; final Pool<PendingEvent> mPendingEventPool = new SimplePool<PendingEvent>(20); final SparseArray<PendingEvent> mPendingEvents = new SparseArray<PendingEvent>(20); @@ -446,8 +429,8 @@ public final class InputMethodManager { return; } - mCursorAnchorInfoMonitorMode = CURSOR_ANCHOR_INFO_MONITOR_MODE_NONE; - mCursorRectMonitorMode = CURSOR_RECT_MONITOR_MODE_NONE; + mRequestUpdateCursorAnchorInfoMonitorMode = + REQUEST_UPDATE_CURSOR_ANCHOR_INFO_NONE; setInputChannelLocked(res.channel); mCurMethod = res.method; @@ -1540,59 +1523,49 @@ public final class InputMethodManager { } /** - * Returns true if the current input method wants to watch the location + * Return true if the current input method wants to watch the location * of the input editor's cursor in its window. - */ - public boolean isWatchingCursor(View view) { - if (!isActive(view)) { - return false; - } - synchronized (mH) { - return (mCursorRectMonitorMode & CursorAnchorInfoRequest.FLAG_CURSOR_RECT_MONITOR) != 0; - } - } - - /** - * Updates the result of {@link #isWatchingCursor(View)}. * - * @hide + * @deprecated Use {@link InputConnection#requestUpdateCursorAnchorInfo(int)} instead. */ - public void setCursorRectMonitorMode(int flags) { - synchronized (mH) { - mCursorRectMonitorMode = (CURSOR_RECT_MONITOR_FLAG_MASK & flags); - } + @Deprecated + public boolean isWatchingCursor(View view) { + return false; } /** - * Returns true if the current input method wants to be notified when cursor/anchor location + * Return true if the current input method wants to be notified when cursor/anchor location * is changed. * * @hide */ public boolean isCursorAnchorInfoEnabled() { synchronized (mH) { - final boolean isImmediate = (mCursorAnchorInfoMonitorMode & - CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE) != 0; - final boolean isMonitoring = (mCursorAnchorInfoMonitorMode & - CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_MONITOR) != 0; + final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode & + InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE) != 0; + final boolean isMonitoring = (mRequestUpdateCursorAnchorInfoMonitorMode & + InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_MONITOR) != 0; return isImmediate || isMonitoring; } } /** - * Updates the result of {@link #isWatchingCursor(View)}. + * Set the requested mode for {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)}. * * @hide */ - public void setCursorAnchorInfoMonitorMode(int flags) { + public void setUpdateCursorAnchorInfoMode(int flags) { synchronized (mH) { - mCursorAnchorInfoMonitorMode = (CURSOR_ANCHOR_INFO_MONITOR_FLAG_MASK & flags); + mRequestUpdateCursorAnchorInfoMonitorMode = flags; } } /** * Report the current cursor location in its window. + * + * @deprecated Use {@link #updateCursorAnchorInfo(View, CursorAnchorInfo)} instead. */ + @Deprecated public void updateCursor(View view, int left, int top, int right, int bottom) { checkFocus(); synchronized (mH) { @@ -1601,33 +1574,15 @@ public final class InputMethodManager { || mCurrentTextBoxAttribute == null || mCurMethod == null) { return; } - if (DEBUG) Log.d(TAG, "updateCursor"); - final boolean usesScreenCoordinates = (mCursorRectMonitorMode & - CursorAnchorInfoRequest.FLAG_CURSOR_RECT_IN_SCREEN_COORDINATES) != 0; - if (usesScreenCoordinates) { - view.getLocationOnScreen(mViewTopLeft); - final Matrix viewMatrix = view.getMatrix(); - final boolean usesViewMatrix = (viewMatrix != null) && ((mCursorRectMonitorMode & - CursorAnchorInfoRequest.FLAG_CURSOR_RECT_WITH_VIEW_MATRIX) != 0); - if (usesViewMatrix) { - mTempRectF.set(left, top, right, bottom); - mViewToScreenMatrix.set(viewMatrix); - mViewToScreenMatrix.postTranslate(mViewTopLeft[0], mViewTopLeft[1]); - mViewToScreenMatrix.mapRect(mTempRectF); - mNextCursorRect.set((int)mTempRectF.left, (int)mTempRectF.top, - (int)mTempRectF.right, (int)mTempRectF.bottom); - } else { - mNextCursorRect.set(left + mViewTopLeft[0], top + mViewTopLeft[1], - right + mViewTopLeft[0], bottom + mViewTopLeft[1]); - } - } else { - mNextCursorRect.set(left, top, right, bottom); - } - if (!Objects.equals(mCursorRect, mNextCursorRect)) { - if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mNextCursorRect); + + mTmpCursorRect.set(left, top, right, bottom); + if (!mCursorRect.equals(mTmpCursorRect)) { + if (DEBUG) Log.d(TAG, "updateCursor"); + try { - mCurMethod.updateCursor(mNextCursorRect); - mCursorRect.set(mNextCursorRect); + if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod); + mCurMethod.updateCursor(mTmpCursorRect); + mCursorRect.set(mTmpCursorRect); } catch (RemoteException e) { Log.w(TAG, "IME died: " + mCurId, e); } @@ -1652,8 +1607,8 @@ public final class InputMethodManager { } // If immediate bit is set, we will call updateCursorAnchorInfo() even when the data has // not been changed from the previous call. - final boolean isImmediate = (mCursorAnchorInfoMonitorMode & - CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE) != 0; + final boolean isImmediate = (mRequestUpdateCursorAnchorInfoMonitorMode & + InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE) != 0; if (!isImmediate && Objects.equals(mCursorAnchorInfo, cursorAnchorInfo)) { // TODO: Consider always emitting this message once we have addressed redundant // calls of this method from android.widget.Editor. @@ -1668,8 +1623,8 @@ public final class InputMethodManager { mCurMethod.updateCursorAnchorInfo(cursorAnchorInfo); mCursorAnchorInfo = cursorAnchorInfo; // Clear immediate bit (if any). - mCursorAnchorInfoMonitorMode &= - ~CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE; + mRequestUpdateCursorAnchorInfoMonitorMode &= + ~InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE; } catch (RemoteException e) { Log.w(TAG, "IME died: " + mCurId, e); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index 60ef693..eb93745 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -65,7 +65,6 @@ import android.view.animation.LinearInterpolator; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; -import android.view.inputmethod.CursorAnchorInfoRequest; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; @@ -5718,8 +5717,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } @Override - public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) { - return getTarget().requestCursorAnchorInfo(request); + public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { + return getTarget().requestUpdateCursorAnchorInfo(cursorUpdateMode); } } diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 3f5f045..29c8298 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -1298,25 +1298,6 @@ public class Editor { reported = reportExtractedText(); } } - - if (imm.isWatchingCursor(mTextView) && highlight != null) { - highlight.computeBounds(ims.mTmpRectF, true); - ims.mTmpOffset[0] = ims.mTmpOffset[1] = 0; - - canvas.getMatrix().mapPoints(ims.mTmpOffset); - ims.mTmpRectF.offset(ims.mTmpOffset[0], ims.mTmpOffset[1]); - - ims.mTmpRectF.offset(0, cursorOffsetVertical); - - ims.mCursorRectInWindow.set((int)(ims.mTmpRectF.left + 0.5), - (int)(ims.mTmpRectF.top + 0.5), - (int)(ims.mTmpRectF.right + 0.5), - (int)(ims.mTmpRectF.bottom + 0.5)); - - imm.updateCursor(mTextView, - ims.mCursorRectInWindow.left, ims.mCursorRectInWindow.top, - ims.mCursorRectInWindow.right, ims.mCursorRectInWindow.bottom); - } } } @@ -4136,7 +4117,6 @@ public class Editor { static class InputMethodState { Rect mCursorRectInWindow = new Rect(); - RectF mTmpRectF = new RectF(); float[] mTmpOffset = new float[2]; ExtractedTextRequest mExtractedTextRequest; final ExtractedText mExtractedText = new ExtractedText(); diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index 8ee0a1b..cb0c3d0 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -230,16 +230,29 @@ public class VideoView extends SurfaceView mTargetState = STATE_IDLE; } + /** + * Sets video path. + * + * @param path the path of the video. + */ public void setVideoPath(String path) { setVideoURI(Uri.parse(path)); } + /** + * Sets video URI. + * + * @param uri the URI of the video. + */ public void setVideoURI(Uri uri) { setVideoURI(uri, null); } /** - * @hide + * Sets video URI using specific headers. + * + * @param uri the URI of the video. + * @param headers the headers for the URI request. */ public void setVideoURI(Uri uri, Map<String, String> headers) { mUri = uri; diff --git a/core/java/com/android/internal/app/LocalePicker.java b/core/java/com/android/internal/app/LocalePicker.java index 229df8f..1f0bb76 100644 --- a/core/java/com/android/internal/app/LocalePicker.java +++ b/core/java/com/android/internal/app/LocalePicker.java @@ -27,6 +27,7 @@ import android.content.res.Configuration; import android.content.res.Resources; import android.os.Bundle; import android.os.RemoteException; +import android.provider.Settings; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -50,10 +51,6 @@ public class LocalePicker extends ListFragment { public void onLocaleSelected(Locale locale); } - protected boolean isInDeveloperMode() { - return false; - } - LocaleSelectionListener mListener; // default to null public static class LocaleInfo implements Comparable<LocaleInfo> { @@ -86,40 +83,17 @@ public class LocalePicker extends ListFragment { } } - /** - * Constructs an Adapter object containing Locale information. Content is sorted by - * {@link LocaleInfo#label}. - */ - public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) { - return constructAdapter(context, false /* disable pesudolocales */); - } - - public static ArrayAdapter<LocaleInfo> constructAdapter(Context context, - final boolean isInDeveloperMode) { - return constructAdapter(context, R.layout.locale_picker_item, R.id.locale, - isInDeveloperMode); - } - - public static ArrayAdapter<LocaleInfo> constructAdapter(Context context, - final int layoutId, final int fieldId) { - return constructAdapter(context, layoutId, fieldId, false /* disable pseudolocales */); - } - public static List<LocaleInfo> getAllAssetLocales(Context context, boolean isInDeveloperMode) { final Resources resources = context.getResources(); final String[] locales = Resources.getSystem().getAssets().getLocales(); List<String> localeList = new ArrayList<String>(locales.length); Collections.addAll(localeList, locales); - if (isInDeveloperMode) { - if (!localeList.contains("zz_ZZ")) { - localeList.add("zz_ZZ"); - } - /** - TODO: Enable when zz_ZY Pseudolocale is complete - * if (!localeList.contains("zz_ZY")) { - * localeList.add("zz_ZY"); - * } - */ + + // Don't show the pseudolocales unless we're in developer mode. http://b/17190407. + if (!isInDeveloperMode) { + localeList.remove("ar-XB"); + localeList.remove("en-XA"); } Collections.sort(localeList); @@ -160,14 +134,7 @@ public class LocalePicker extends ListFragment { localeInfos.add(new LocaleInfo(toTitleCase( getDisplayName(l, specialLocaleCodes, specialLocaleNames)), l)); } else { - String displayName; - if (locale.equals("zz_ZZ")) { - displayName = "[Developer] Accented English"; - } else if (locale.equals("zz_ZY")) { - displayName = "[Developer] Fake Bi-Directional"; - } else { - displayName = toTitleCase(l.getDisplayLanguage(l)); - } + String displayName = toTitleCase(l.getDisplayLanguage(l)); if (DEBUG) { Log.v(TAG, "adding "+displayName); } @@ -180,8 +147,18 @@ public class LocalePicker extends ListFragment { return localeInfos; } + /** + * Constructs an Adapter object containing Locale information. Content is sorted by + * {@link LocaleInfo#label}. + */ + public static ArrayAdapter<LocaleInfo> constructAdapter(Context context) { + return constructAdapter(context, R.layout.locale_picker_item, R.id.locale); + } + public static ArrayAdapter<LocaleInfo> constructAdapter(Context context, - final int layoutId, final int fieldId, final boolean isInDeveloperMode) { + final int layoutId, final int fieldId) { + boolean isInDeveloperMode = Settings.Global.getInt(context.getContentResolver(), + Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) != 0; final List<LocaleInfo> localeInfos = getAllAssetLocales(context, isInDeveloperMode); final LayoutInflater inflater = @@ -232,8 +209,7 @@ public class LocalePicker extends ListFragment { @Override public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity(), - isInDeveloperMode()); + final ArrayAdapter<LocaleInfo> adapter = constructAdapter(getActivity()); setListAdapter(adapter); } diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 897381d..b1f5d90 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -25,7 +25,6 @@ import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; -import android.view.inputmethod.CursorAnchorInfoRequest; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; @@ -55,7 +54,7 @@ public class IInputConnectionWrapper extends IInputContext.Stub { private static final int DO_REPORT_FULLSCREEN_MODE = 100; private static final int DO_PERFORM_PRIVATE_COMMAND = 120; private static final int DO_CLEAR_META_KEY_STATES = 130; - private static final int DO_REQUEST_CURSOR_ANCHOR_INFO = 140; + private static final int DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO = 140; private WeakReference<InputConnection> mInputConnection; @@ -177,9 +176,10 @@ public class IInputConnectionWrapper extends IInputContext.Stub { dispatchMessage(obtainMessageOO(DO_PERFORM_PRIVATE_COMMAND, action, data)); } - public void requestCursorAnchorInfo(CursorAnchorInfoRequest request, int seq, + public void requestUpdateCursorAnchorInfo(int cursorUpdateMode, int seq, IInputContextCallback callback) { - dispatchMessage(obtainMessageOSC(DO_REQUEST_CURSOR_ANCHOR_INFO, request, seq, callback)); + dispatchMessage(obtainMessageISC(DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO, cursorUpdateMode, + seq, callback)); } void dispatchMessage(Message msg) { @@ -427,18 +427,17 @@ public class IInputConnectionWrapper extends IInputContext.Stub { (Bundle)args.arg2); return; } - case DO_REQUEST_CURSOR_ANCHOR_INFO: { + case DO_REQUEST_UPDATE_CURSOR_ANCHOR_INFO: { SomeArgs args = (SomeArgs)msg.obj; try { InputConnection ic = mInputConnection.get(); if (ic == null || !isActive()) { Log.w(TAG, "requestCursorAnchorInfo on inactive InputConnection"); - args.callback.setRequestCursorAnchorInfoResult(0, args.seq); + args.callback.setRequestUpdateCursorAnchorInfoResult(false, args.seq); return; } - args.callback.setRequestCursorAnchorInfoResult( - ic.requestCursorAnchorInfo((CursorAnchorInfoRequest)args.arg1), - args.seq); + args.callback.setRequestUpdateCursorAnchorInfoResult( + ic.requestUpdateCursorAnchorInfo(msg.arg1), args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling requestCursorAnchorInfo", e); } diff --git a/core/java/com/android/internal/view/IInputContext.aidl b/core/java/com/android/internal/view/IInputContext.aidl index c06596a..fd2b513 100644 --- a/core/java/com/android/internal/view/IInputContext.aidl +++ b/core/java/com/android/internal/view/IInputContext.aidl @@ -20,7 +20,6 @@ import android.os.Bundle; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; -import android.view.inputmethod.CursorAnchorInfoRequest; import android.view.inputmethod.ExtractedTextRequest; import com.android.internal.view.IInputContextCallback; @@ -74,6 +73,6 @@ import com.android.internal.view.IInputContextCallback; void getSelectedText(int flags, int seq, IInputContextCallback callback); - void requestCursorAnchorInfo(in CursorAnchorInfoRequest request, int seq, + void requestUpdateCursorAnchorInfo(in int cursorUpdateMode, int seq, IInputContextCallback callback); } diff --git a/core/java/com/android/internal/view/IInputContextCallback.aidl b/core/java/com/android/internal/view/IInputContextCallback.aidl index ab2fbdc..54ea306 100644 --- a/core/java/com/android/internal/view/IInputContextCallback.aidl +++ b/core/java/com/android/internal/view/IInputContextCallback.aidl @@ -27,5 +27,5 @@ oneway interface IInputContextCallback { void setCursorCapsMode(int capsMode, int seq); void setExtractedText(in ExtractedText extractedText, int seq); void setSelectedText(CharSequence selectedText, int seq); - void setRequestCursorAnchorInfoResult(int result, int seq); + void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq); } diff --git a/core/java/com/android/internal/view/InputConnectionWrapper.java b/core/java/com/android/internal/view/InputConnectionWrapper.java index 8535a98..a8526c8 100644 --- a/core/java/com/android/internal/view/InputConnectionWrapper.java +++ b/core/java/com/android/internal/view/InputConnectionWrapper.java @@ -23,7 +23,6 @@ import android.util.Log; import android.view.KeyEvent; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; -import android.view.inputmethod.CursorAnchorInfoRequest; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; import android.view.inputmethod.InputConnection; @@ -41,7 +40,7 @@ public class InputConnectionWrapper implements InputConnection { public CharSequence mSelectedText; public ExtractedText mExtractedText; public int mCursorCapsMode; - public int mCursorAnchorInfoRequestResult; + public boolean mRequestUpdateCursorAnchorInfoResult; // A 'pool' of one InputContextCallback. Each ICW request will attempt to gain // exclusive access to this object. @@ -155,10 +154,10 @@ public class InputConnectionWrapper implements InputConnection { } } - public void setRequestCursorAnchorInfoResult(int result, int seq) { + public void setRequestUpdateCursorAnchorInfoResult(boolean result, int seq) { synchronized (this) { if (seq == mSeq) { - mCursorAnchorInfoRequestResult = result; + mRequestUpdateCursorAnchorInfoResult = result; mHaveValue = true; notifyAll(); } else { @@ -429,21 +428,21 @@ public class InputConnectionWrapper implements InputConnection { } } - public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) { - int value = CursorAnchorInfoRequest.RESULT_NOT_HANDLED; + public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { + boolean result = false; try { InputContextCallback callback = InputContextCallback.getInstance(); - mIInputContext.requestCursorAnchorInfo(request, callback.mSeq, callback); + mIInputContext.requestUpdateCursorAnchorInfo(cursorUpdateMode, callback.mSeq, callback); synchronized (callback) { callback.waitForResultLocked(); if (callback.mHaveValue) { - value = callback.mCursorAnchorInfoRequestResult; + result = callback.mRequestUpdateCursorAnchorInfoResult; } } callback.dispose(); } catch (RemoteException e) { - return CursorAnchorInfoRequest.RESULT_NOT_HANDLED; + return false; } - return value; + return result; } } diff --git a/core/java/com/android/internal/view/menu/ActionMenuItemView.java b/core/java/com/android/internal/view/menu/ActionMenuItemView.java index 891baea..7eec392 100644 --- a/core/java/com/android/internal/view/menu/ActionMenuItemView.java +++ b/core/java/com/android/internal/view/menu/ActionMenuItemView.java @@ -265,13 +265,15 @@ public class ActionMenuItemView extends TextView final int width = getWidth(); final int height = getHeight(); final int midy = screenPos[1] + height / 2; - final int screenWidth = context.getResources().getDisplayMetrics().widthPixels; - + int referenceX = screenPos[0] + width / 2; + if (v.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) { + final int screenWidth = context.getResources().getDisplayMetrics().widthPixels; + referenceX = screenWidth - referenceX; // mirror + } Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT); if (midy < displayFrame.height()) { // Show along the top; follow action buttons - cheatSheet.setGravity(Gravity.TOP | Gravity.END, - screenWidth - screenPos[0] - width / 2, height); + cheatSheet.setGravity(Gravity.TOP | Gravity.END, referenceX, height); } else { // Show along the bottom center cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height); diff --git a/core/java/com/android/internal/widget/EditableInputConnection.java b/core/java/com/android/internal/widget/EditableInputConnection.java index bfe0090..2967938 100644 --- a/core/java/com/android/internal/widget/EditableInputConnection.java +++ b/core/java/com/android/internal/widget/EditableInputConnection.java @@ -25,9 +25,9 @@ import android.util.Log; import android.view.inputmethod.BaseInputConnection; import android.view.inputmethod.CompletionInfo; import android.view.inputmethod.CorrectionInfo; -import android.view.inputmethod.CursorAnchorInfoRequest; import android.view.inputmethod.ExtractedText; import android.view.inputmethod.ExtractedTextRequest; +import android.view.inputmethod.InputConnection; import android.widget.TextView; public class EditableInputConnection extends BaseInputConnection { @@ -188,24 +188,17 @@ public class EditableInputConnection extends BaseInputConnection { } @Override - public int requestCursorAnchorInfo(CursorAnchorInfoRequest request) { - if (DEBUG) Log.v(TAG, "requestCursorAnchorInfo " + request); - - // This implementation supports TYPE_CURSOR_ANCHOR_INFO only. Other events will be - // delegated to the super class. - if (request == null || - request.getRequestType() != CursorAnchorInfoRequest.TYPE_CURSOR_ANCHOR_INFO) { - return super.requestCursorAnchorInfo(request); - } + public boolean requestUpdateCursorAnchorInfo(int cursorUpdateMode) { + if (DEBUG) Log.v(TAG, "requestUpdateCursorAnchorInfo " + cursorUpdateMode); + if (mIMM == null) { // In this case, TYPE_CURSOR_ANCHOR_INFO is not handled. - // TODO: Return some notification code for the input method that indicates + // TODO: Return some notification code rather than false to indicate method that // CursorAnchorInfo is temporarily unavailable. - return CursorAnchorInfoRequest.RESULT_NOT_HANDLED; + return false; } - final int flags = request.getRequestFlags(); - mIMM.setCursorAnchorInfoMonitorMode(flags); - if ((flags & CursorAnchorInfoRequest.FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE) != 0) { + mIMM.setUpdateCursorAnchorInfoMode(cursorUpdateMode); + if ((cursorUpdateMode & InputConnection.REQUEST_UPDATE_CURSOR_ANCHOR_INFO_IMMEDIATE) != 0) { if (mTextView == null) { // In this case, FLAG_CURSOR_ANCHOR_INFO_IMMEDIATE is silently ignored. // TODO: Return some notification code for the input method that indicates @@ -220,6 +213,6 @@ public class EditableInputConnection extends BaseInputConnection { mTextView.requestLayout(); } } - return CursorAnchorInfoRequest.RESULT_SCHEDULED; + return true; } } diff --git a/core/java/com/android/internal/widget/LockPatternUtils.java b/core/java/com/android/internal/widget/LockPatternUtils.java index 74b1fdd..6dec036 100644 --- a/core/java/com/android/internal/widget/LockPatternUtils.java +++ b/core/java/com/android/internal/widget/LockPatternUtils.java @@ -479,6 +479,7 @@ public class LockPatternUtils { saveLockPattern(null); setLong(PASSWORD_TYPE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); setLong(PASSWORD_TYPE_ALTERNATE_KEY, DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED); + onAfterChangingPassword(); } /** @@ -565,6 +566,7 @@ public class LockPatternUtils { dpm.setActivePasswordState(DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userId); } + onAfterChangingPassword(); } catch (RemoteException re) { Log.e(TAG, "Couldn't save lock pattern " + re); } @@ -844,6 +846,7 @@ public class LockPatternUtils { DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED, 0, 0, 0, 0, 0, 0, 0, userHandle); } + onAfterChangingPassword(); } catch (RemoteException re) { // Cant do much Log.e(TAG, "Unable to save lock password " + re); @@ -1561,4 +1564,8 @@ public class LockPatternUtils { public void requireCredentialEntry(int userId) { getTrustManager().reportRequireCredentialEntry(userId); } + + private void onAfterChangingPassword() { + getTrustManager().reportEnabledTrustAgentsChanged(getCurrentOrCallingUserId()); + } } diff --git a/core/java/com/android/server/backup/SystemBackupAgent.java b/core/java/com/android/server/backup/SystemBackupAgent.java new file mode 100644 index 0000000..26e2e2a --- /dev/null +++ b/core/java/com/android/server/backup/SystemBackupAgent.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.backup; + + +import android.app.IWallpaperManager; +import android.app.backup.BackupDataInput; +import android.app.backup.BackupDataOutput; +import android.app.backup.BackupAgentHelper; +import android.app.backup.FullBackup; +import android.app.backup.FullBackupDataOutput; +import android.app.backup.WallpaperBackupHelper; +import android.content.Context; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.UserHandle; +import android.util.Slog; + +import java.io.File; +import java.io.IOException; + +/** + * Backup agent for various system-managed data, currently just the system wallpaper + */ +public class SystemBackupAgent extends BackupAgentHelper { + private static final String TAG = "SystemBackupAgent"; + + // These paths must match what the WallpaperManagerService uses. The leaf *_FILENAME + // are also used in the full-backup file format, so must not change unless steps are + // taken to support the legacy backed-up datasets. + private static final String WALLPAPER_IMAGE_FILENAME = "wallpaper"; + private static final String WALLPAPER_INFO_FILENAME = "wallpaper_info.xml"; + + // TODO: Will need to change if backing up non-primary user's wallpaper + private static final String WALLPAPER_IMAGE_DIR = + Environment.getUserSystemDirectory(UserHandle.USER_OWNER).getAbsolutePath(); + private static final String WALLPAPER_IMAGE = WallpaperBackupHelper.WALLPAPER_IMAGE; + + // TODO: Will need to change if backing up non-primary user's wallpaper + private static final String WALLPAPER_INFO_DIR = + Environment.getUserSystemDirectory(UserHandle.USER_OWNER).getAbsolutePath(); + private static final String WALLPAPER_INFO = WallpaperBackupHelper.WALLPAPER_INFO; + // Use old keys to keep legacy data compatibility and avoid writing two wallpapers + private static final String WALLPAPER_IMAGE_KEY = WallpaperBackupHelper.WALLPAPER_IMAGE_KEY; + private static final String WALLPAPER_INFO_KEY = WallpaperBackupHelper.WALLPAPER_INFO_KEY; + + @Override + public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) throws IOException { + // We only back up the data under the current "wallpaper" schema with metadata + IWallpaperManager wallpaper = (IWallpaperManager)ServiceManager.getService( + Context.WALLPAPER_SERVICE); + String[] files = new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO }; + String[] keys = new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY }; + if (wallpaper != null) { + try { + final String wallpaperName = wallpaper.getName(); + if (wallpaperName != null && wallpaperName.length() > 0) { + // When the wallpaper has a name, back up the info by itself. + // TODO: Don't rely on the innards of the service object like this! + // TODO: Send a delete for any stored wallpaper image in this case? + files = new String[] { WALLPAPER_INFO }; + keys = new String[] { WALLPAPER_INFO_KEY }; + } + } catch (RemoteException re) { + Slog.e(TAG, "Couldn't get wallpaper name\n" + re); + } + } + addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, files, keys)); + super.onBackup(oldState, data, newState); + } + + @Override + public void onFullBackup(FullBackupDataOutput data) throws IOException { + // At present we back up only the wallpaper + fullWallpaperBackup(data); + } + + private void fullWallpaperBackup(FullBackupDataOutput output) { + // Back up the data files directly. We do them in this specific order -- + // info file followed by image -- because then we need take no special + // steps during restore; the restore will happen properly when the individual + // files are restored piecemeal. + FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null, + WALLPAPER_INFO_DIR, WALLPAPER_INFO, output.getData()); + FullBackup.backupToTar(getPackageName(), FullBackup.ROOT_TREE_TOKEN, null, + WALLPAPER_IMAGE_DIR, WALLPAPER_IMAGE, output.getData()); + } + + @Override + public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) + throws IOException { + // On restore, we also support a previous data schema "system_files" + addHelper("wallpaper", new WallpaperBackupHelper(SystemBackupAgent.this, + new String[] { WALLPAPER_IMAGE, WALLPAPER_INFO }, + new String[] { WALLPAPER_IMAGE_KEY, WALLPAPER_INFO_KEY} )); + addHelper("system_files", new WallpaperBackupHelper(SystemBackupAgent.this, + new String[] { WALLPAPER_IMAGE }, + new String[] { WALLPAPER_IMAGE_KEY} )); + + try { + super.onRestore(data, appVersionCode, newState); + + IWallpaperManager wallpaper = (IWallpaperManager) ServiceManager.getService( + Context.WALLPAPER_SERVICE); + if (wallpaper != null) { + try { + wallpaper.settingsRestored(); + } catch (RemoteException re) { + Slog.e(TAG, "Couldn't restore settings\n" + re); + } + } + } catch (IOException ex) { + // If there was a failure, delete everything for the wallpaper, this is too aggressive, + // but this is hopefully a rare failure. + Slog.d(TAG, "restore failed", ex); + (new File(WALLPAPER_IMAGE)).delete(); + (new File(WALLPAPER_INFO)).delete(); + } + } + + @Override + public void onRestoreFile(ParcelFileDescriptor data, long size, + int type, String domain, String path, long mode, long mtime) + throws IOException { + Slog.i(TAG, "Restoring file domain=" + domain + " path=" + path); + + // Bits to indicate postprocessing we may need to perform + boolean restoredWallpaper = false; + + File outFile = null; + // Various domain+files we understand a priori + if (domain.equals(FullBackup.ROOT_TREE_TOKEN)) { + if (path.equals(WALLPAPER_INFO_FILENAME)) { + outFile = new File(WALLPAPER_INFO); + restoredWallpaper = true; + } else if (path.equals(WALLPAPER_IMAGE_FILENAME)) { + outFile = new File(WALLPAPER_IMAGE); + restoredWallpaper = true; + } + } + + try { + if (outFile == null) { + Slog.w(TAG, "Skipping unrecognized system file: [ " + domain + " : " + path + " ]"); + } + FullBackup.restoreFile(data, size, type, mode, mtime, outFile); + + if (restoredWallpaper) { + IWallpaperManager wallpaper = + (IWallpaperManager)ServiceManager.getService( + Context.WALLPAPER_SERVICE); + if (wallpaper != null) { + try { + wallpaper.settingsRestored(); + } catch (RemoteException re) { + Slog.e(TAG, "Couldn't restore settings\n" + re); + } + } + } + } catch (IOException e) { + if (restoredWallpaper) { + // Make sure we wind up in a good state + (new File(WALLPAPER_IMAGE)).delete(); + (new File(WALLPAPER_INFO)).delete(); + } + } + } +} |