import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.Voicemail;
-
import java.util.List;
/**
"android.intent.action.VOICEMAIL_SMS_RECEIVED";
/**
- * Extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to indicate the
- * event type of the SMS. Common values are "SYNC" or "STATUS"
+ * Optional extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to
+ * indicate the event type of the SMS. Common values are "SYNC" or "STATUS". The extra will not
+ * exist if the framework cannot parse the SMS as voicemail but the carrier pattern indicates
+ * it is.
*/
/** @hide */
public static final String EXTRA_VOICEMAIL_SMS_PREFIX =
"com.android.voicemail.extra.VOICEMAIL_SMS_PREFIX";
/**
- * Extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to indicate the
- * fields sent by the SMS
+ * Optional extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to
+ * indicate the fields sent by the SMS. The extra will not exist if the framework cannot
+ * parse the SMS as voicemail but the carrier pattern indicates it is.
*/
/** @hide */
public static final String EXTRA_VOICEMAIL_SMS_FIELDS =
"com.android.voicemail.extra.VOICEMAIL_SMS_FIELDS";
/**
+ * Extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to indicate the
+ * message body of the SMS. This extra is included if the framework cannot
+ * parse the SMS as voicemail but the carrier pattern indicates it is.
+ */
+ /**
+ * @hide
+ */
+ public static final String EXTRA_VOICEMAIL_SMS_MESSAGE_BODY =
+ "com.android.voicemail.extra.VOICEMAIL_SMS_MESSAGE_BODY";
+
+ /**
* Extra included in {@link #ACTION_VOICEMAIL_SMS_RECEIVED} broadcast intents to indicate he
* subscription ID of the phone account that received the SMS.
*/
// remove it from the transparent region.
final int[] location = attachInfo.mTransparentLocation;
getLocationInWindow(location);
- region.op(location[0], location[1], location[0] + mRight - mLeft,
- location[1] + mBottom - mTop, Region.Op.DIFFERENCE);
+ // When a view has Z value, then it will be better to leave some area below the view
+ // for drawing shadow. The shadow outset is proportional to the Z value. Note that
+ // the bottom part needs more offset than the left, top and right parts due to the
+ // spot light effects.
+ int shadowOffset = getZ() > 0 ? (int) getZ() : 0;
+ region.op(location[0] - shadowOffset, location[1] - shadowOffset,
+ location[0] + mRight - mLeft + shadowOffset,
+ location[1] + mBottom - mTop + (shadowOffset * 3), Region.Op.DIFFERENCE);
} else {
if (mBackground != null && mBackground.getOpacity() != PixelFormat.TRANSPARENT) {
// The SKIP_DRAW flag IS set and the background drawable exists, we remove
return true;
}
super.gatherTransparentRegion(region);
- final View[] children = mChildren;
- final int count = mChildrenCount;
+ // Instead of naively traversing the view tree, we have to traverse according to the Z
+ // order here. We need to go with the same order as dispatchDraw().
+ // One example is that after surfaceView punch a hole, we will still allow other views drawn
+ // on top of that hole. In this case, those other views should be able to cut the
+ // transparent region into smaller area.
+ final int childrenCount = mChildrenCount;
boolean noneOfTheChildrenAreTransparent = true;
- for (int i = 0; i < count; i++) {
- final View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
- if (!child.gatherTransparentRegion(region)) {
- noneOfTheChildrenAreTransparent = false;
+ if (childrenCount > 0) {
+ final ArrayList<View> preorderedList = buildOrderedChildList();
+ final boolean customOrder = preorderedList == null
+ && isChildrenDrawingOrderEnabled();
+ final View[] children = mChildren;
+ for (int i = 0; i < childrenCount; i++) {
+ final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
+ final View child = getAndVerifyPreorderedView(preorderedList, children, childIndex);
+ if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
+ if (!child.gatherTransparentRegion(region)) {
+ noneOfTheChildrenAreTransparent = false;
+ }
}
}
+ if (preorderedList != null) preorderedList.clear();
}
return meOpaque || noneOfTheChildrenAreTransparent;
}
}
// Let the window manager know to align the top to y.
- outParams.gravity = Gravity.LEFT | Gravity.TOP;
+ outParams.gravity = computeGravity();
outParams.width = width;
outParams.height = height;
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ ** Copyright 2016, 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.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>23203</item>
+ <item>23205</item>
+ </string-array>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ ** Copyright 2016, 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.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>23203</item>
+ </string-array>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ ** Copyright 2016, 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.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>302</item>
+ </string-array>
+</resources>
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ ** Copyright 2016, 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.
+ */
+-->
+
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+ <!-- Don't use roaming icon for considered operators -->
+ <string-array translatable="false" name="config_operatorConsideredNonRoaming">
+ <item>302</item>
+ </string-array>
+</resources>
<!-- The threshold angle for any motion detection in auto-power save modes.
In hundreths of a degree. -->
- <integer name="config_autoPowerModeThresholdAngle">200</integer>
+ <integer name="config_autoPowerModeThresholdAngle">10</integer>
<!-- The sensor id of an "any motion" sensor used in auto-power save modes.
0 indicates this sensor is not available. -->
<string name="config_networkOverLimitComponent" translatable="false">com.android.systemui/com.android.systemui.net.NetworkOverLimitActivity</string>
<string name="config_dataUsageSummaryComponent" translatable="false">com.android.settings/com.android.settings.Settings$DataUsageSummaryActivity</string>
+
+ <!-- A array of regex to treat a SMS as VVM SMS if the message body matches.
+ Each item represents an entry, which consists of two parts:
+ a comma (,) separated list of MCCMNC the regex applies to, followed by a semicolon (;), and
+ then the regex itself. -->
+ <string-array translatable="false" name="config_vvmSmsFilterRegexes">
+ <!-- Verizon requires any SMS that starts with //VZWVVM to be treated as a VVM SMS-->
+ <item>310004,310010,310012,310013,310590,310890,310910,311110,311270,311271,311272,311273,311274,311275,311276,311277,311278,311279,311280,311281,311282,311283,311284,311285,311286,311287,311288,311289,311390,311480,311481,311482,311483,311484,311485,311486,311487,311488,311489;^//VZWVVM.*</item>
+ </string-array>
</resources>
<java-symbol type="string" name="prohibit_manual_network_selection_in_gobal_mode" />
<java-symbol type="id" name="profile_button" />
+ <java-symbol type="array" name="config_vvmSmsFilterRegexes" />
+
<!-- Cascading submenus -->
<java-symbol type="dimen" name="cascading_menus_min_smallest_width" />
to: /topic/libraries/support-library/index.html
- from: /billions
to: /topic/billions/index.html
+- from: /performance
+ to: /topic/performance/index.html
- from: /tools/extras/support-library.html
to: /topic/libraries/support-library/index.html
- from: /training/basics/fragments/support-lib.html
* android:valueType="pathType"/>
* </set>
* </pre></li>
+ * <p>
+ * Since AAPT tool is now supporting a new format which can bundle several related XML files into
+ * one, we can merge the previous example into one XML file, like this:
+ * </p>
+ * <pre>
+ * <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" >
+ * <aapt:attr name="android:drawable">
+ * <vector
+ * android:height="64dp"
+ * android:width="64dp"
+ * android:viewportHeight="600"
+ * android:viewportWidth="600" >
+ * <group
+ * android:name="rotationGroup"
+ * android:pivotX="300.0"
+ * android:pivotY="300.0"
+ * android:rotation="45.0" >
+ * <path
+ * android:name="v"
+ * android:fillColor="#000000"
+ * android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
+ * </group>
+ * </vector>
+ * </aapt:attr>
+ *
+ * <target android:name="rotationGroup"> *
+ * <aapt:attr name="android:animation">
+ * <objectAnimator
+ * android:duration="6000"
+ * android:propertyName="rotation"
+ * android:valueFrom="0"
+ * android:valueTo="360" />
+ * </aapt:attr>
+ * </target>
+ *
+ * <target android:name="v" >
+ * <aapt:attr name="android:animation">
+ * <set>
+ * <objectAnimator
+ * android:duration="3000"
+ * android:propertyName="pathData"
+ * android:valueFrom="M300,70 l 0,-70 70,70 0,0 -70,70z"
+ * android:valueTo="M300,70 l 0,-70 70,0 0,140 -70,0 z"
+ * android:valueType="pathType"/>
+ * </set>
+ * </aapt:attr>
+ * </target>
+ * </animated-vector>
+ * </pre>
*
* @attr ref android.R.styleable#AnimatedVectorDrawable_drawable
* @attr ref android.R.styleable#AnimatedVectorDrawableTarget_name
import android.graphics.ColorFilter;
import android.graphics.Insets;
import android.graphics.PixelFormat;
+import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect;
-import android.graphics.PorterDuff.Mode;
import android.graphics.Shader;
import android.util.ArrayMap;
import android.util.AttributeSet;
* in the SVG's path data. This is defined in the viewport space.</dd>
* <dt><code>android:fillColor</code></dt>
* <dd>Specifies the color used to fill the path. May be a color or, for SDK 24+, a color state list
- * or a gradient color. If this property is animated, any value set by the animation will
- * override the original value. No path fill is drawn if this property is not specified.</dd>
+ * or a gradient color (See {@link android.R.styleable#GradientColor}
+ * and {@link android.R.styleable#GradientColorItem}).
+ * If this property is animated, any value set by the animation will override the original value.
+ * No path fill is drawn if this property is not specified.</dd>
* <dt><code>android:strokeColor</code></dt>
* <dd>Specifies the color used to draw the path outline. May be a color or, for SDK 24+, a color
- * state list or a gradient color. If this property is animated, any value set by the animation will
- * override the original value. No path outline is drawn if this property is not specified.</dd>
+ * state list or a gradient color (See {@link android.R.styleable#GradientColor}
+ * and {@link android.R.styleable#GradientColorItem}).
+ * If this property is animated, any value set by the animation will override the original value.
+ * No path outline is drawn if this property is not specified.</dd>
* <dt><code>android:strokeWidth</code></dt>
* <dd>The width a path stroke.</dd>
* <dt><code>android:strokeAlpha</code></dt>
* <dt><code>android:strokeMiterLimit</code></dt>
* <dd>Sets the Miter limit for a stroked path.</dd>
* <dt><code>android:fillType</code></dt>
- * <dd>Sets the fillType for a path. It is the same as SVG's "fill-rule" properties.
- * For more details, see https://www.w3.org/TR/SVG/painting.html#FillRuleProperty</dd>
+ * <dd>Sets the fillType for a path. The types can be either "evenOdd" or "nonZero". They behave the
+ * same as SVG's "fill-rule" properties. For more details, see
+ * <a href="https://www.w3.org/TR/SVG/painting.html#FillRuleProperty">FillRuleProperty</a></dd>
* </dl></dd>
* </dl>
*
* android:pathData="M300,70 l 0,-70 70,70 0,0 -70,70z" />
* </group>
* </vector>
- * </pre></li>
+ * </pre>
+ * </li>
+ * <li>And here is an example of linear gradient color, which is supported in SDK 24+.
+ * See more details in {@link android.R.styleable#GradientColor} and
+ * {@link android.R.styleable#GradientColorItem}.
+ * <pre>
+ * <gradient xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:angle="90"
+ * android:startColor="?android:attr/colorPrimary"
+ * android:endColor="?android:attr/colorControlActivated"
+ * android:centerColor="#f00"
+ * android:startX="0"
+ * android:startY="0"
+ * android:endX="100"
+ * android:endY="100"
+ * android:type="linear">
+ * </gradient>
+ * </pre>
+ * </li>
+ *
*/
public class VectorDrawable extends Drawable {
*/
public static boolean isTouchType(int toolType) {
return toolType == MotionEvent.TOOL_TYPE_FINGER
- || toolType == MotionEvent.TOOL_TYPE_STYLUS;
+ || toolType == MotionEvent.TOOL_TYPE_STYLUS
+ || toolType == MotionEvent.TOOL_TYPE_UNKNOWN;
}
/**
<!-- Label for Help and feedback menu item -->
<string name="help_feedback_label">Help & feedback</string>
+ <!-- Content description for drawer menu button [CHAR_LIMIT=30]-->
+ <string name="content_description_menu_button">Menu</string>
+
</resources>
public void showMenuIcon() {
mShowingMenu = true;
getActionBar().setHomeAsUpIndicator(R.drawable.ic_menu);
+ getActionBar().setHomeActionContentDescription(R.string.content_description_menu_button);
getActionBar().setDisplayHomeAsUpEnabled(true);
}
import android.util.AttributeSet;
import android.util.SparseArray;
import android.widget.TextView;
-
import com.android.settingslib.R;
public class AccessPointPreference extends Preference {
private final StateListDrawable mWifiSld;
private final int mBadgePadding;
private final UserBadgeCache mBadgeCache;
-
private TextView mTitleView;
+
private boolean mForSavedNetworks = false;
private AccessPoint mAccessPoint;
private Drawable mBadge;
private int mLevel;
private CharSequence mContentDescription;
+ private int mDefaultIconResId;
static final int[] WIFI_CONNECTION_STRENGTH = {
R.string.accessibility_wifi_one_bar,
refresh();
}
+ public AccessPointPreference(AccessPoint accessPoint, Context context, UserBadgeCache cache,
+ int iconResId, boolean forSavedNetworks) {
+ super(context);
+ mBadgeCache = cache;
+ mAccessPoint = accessPoint;
+ mForSavedNetworks = forSavedNetworks;
+ mAccessPoint.setTag(this);
+ mLevel = -1;
+ mDefaultIconResId = iconResId;
+
+ mWifiSld = (StateListDrawable) context.getTheme()
+ .obtainStyledAttributes(wifi_signal_attributes).getDrawable(0);
+
+ // Distance from the end of the title at which this AP's user badge should sit.
+ mBadgePadding = context.getResources()
+ .getDimensionPixelSize(R.dimen.wifi_preference_badge_padding);
+ }
+
public AccessPoint getAccessPoint() {
return mAccessPoint;
}
protected void updateIcon(int level, Context context) {
if (level == -1) {
- setIcon(null);
+ safeSetDefaultIcon();
} else {
if (getIcon() == null) {
// To avoid a drawing race condition, we first set the state (SECURE/NONE) and then
? STATE_SECURED
: STATE_NONE);
Drawable drawable = mWifiSld.getCurrent();
- if (!mForSavedNetworks) {
+ if (!mForSavedNetworks && drawable != null) {
setIcon(drawable);
- } else {
- setIcon(null);
+ return;
}
}
+ safeSetDefaultIcon();
}
}
}
+ private void safeSetDefaultIcon() {
+ if (mDefaultIconResId != 0) {
+ setIcon(mDefaultIconResId);
+ } else {
+ setIcon(null);
+ }
+ }
+
protected void updateBadge(Context context) {
WifiConfiguration config = mAccessPoint.getConfig();
if (config != null) {
<string name="status_bar_no_notifications_title" msgid="4755261167193833213">"Ծանուցումներ չկան"</string>
<string name="status_bar_ongoing_events_title" msgid="1682504513316879202">"Ընթացիկ"</string>
<string name="status_bar_latest_events_title" msgid="6594767438577593172">"Ծանուցումներ"</string>
- <string name="battery_low_title" msgid="6456385927409742437">"Õ\84Õ¡Ö\80Õ¿Õ¯Õ¸Ö\81Õ¨ Õ¬Õ«Ö\81Ö\84Õ¡Õ©Õ¡Ö\83վում է"</string>
+ <string name="battery_low_title" msgid="6456385927409742437">"Õ\84Õ¡Ö\80Õ¿Õ¯Õ¸Ö\81Õ« Õ¬Õ«Ö\81Ö\84Õ¨ Õ½ÕºÕ¡Õ¼վում է"</string>
<string name="battery_low_percent_format" msgid="2900940511201380775">"Մնաց <xliff:g id="PERCENTAGE">%s</xliff:g>"</string>
<string name="battery_low_percent_format_saver_started" msgid="6859235584035338833">"Մնաց <xliff:g id="PERCENTAGE">%s</xliff:g>: Մարտկոցի տնտեսումը միացված է:"</string>
<string name="invalid_charger" msgid="4549105996740522523">"USB լիցքավորումը չի աջակցվում:\nՕգտվեք միայն գործող լիցքավորիչից:"</string>
/** True if an orientation measurement is in progress. */
private boolean mMeasurementInProgress;
+ /** True if sendMessageDelayed() for the mMeasurementTimeout callback has been scheduled */
+ private boolean mMeasurementTimeoutIsActive;
+
+ /** True if sendMessageDelayed() for the mWakelockTimeout callback has been scheduled */
+ private boolean mWakelockTimeoutIsActive;
+
+ /** True if sendMessageDelayed() for the mSensorRestart callback has been scheduled */
+ private boolean mSensorRestartIsActive;
+
/** The most recent gravity vector. */
private Vector3 mCurrentGravityVector = null;
mSensorManager = sm;
mAccelSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
mMeasurementInProgress = false;
+ mMeasurementTimeoutIsActive = false;
+ mWakelockTimeoutIsActive = false;
+ mSensorRestartIsActive = false;
mState = STATE_INACTIVE;
mCallback = callback;
mThresholdAngle = thresholdAngle;
mWakeLock.acquire();
Message wakelockTimeoutMsg = Message.obtain(mHandler, mWakelockTimeout);
mHandler.sendMessageDelayed(wakelockTimeoutMsg, WAKELOCK_TIMEOUT_MILLIS);
+ mWakelockTimeoutIsActive = true;
startOrientationMeasurementLocked();
}
}
mState = STATE_INACTIVE;
if (DEBUG) Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE.");
}
+ mHandler.removeCallbacks(mMeasurementTimeout);
+ mHandler.removeCallbacks(mSensorRestart);
+ mMeasurementTimeoutIsActive = false;
+ mSensorRestartIsActive = false;
if (mMeasurementInProgress) {
mMeasurementInProgress = false;
mSensorManager.unregisterListener(mListener);
}
- mHandler.removeCallbacks(mMeasurementTimeout);
- mHandler.removeCallbacks(mSensorRestart);
mCurrentGravityVector = null;
mPreviousGravityVector = null;
if (mWakeLock.isHeld()) {
- mWakeLock.release();
mHandler.removeCallbacks(mWakelockTimeout);
+ mWakelockTimeoutIsActive = false;
+ mWakeLock.release();
}
}
}
}
Message measurementTimeoutMsg = Message.obtain(mHandler, mMeasurementTimeout);
mHandler.sendMessageDelayed(measurementTimeoutMsg, ACCELEROMETER_DATA_TIMEOUT_MILLIS);
+ mMeasurementTimeoutIsActive = true;
}
}
mMeasurementInProgress);
int status = RESULT_UNKNOWN;
if (mMeasurementInProgress) {
- mSensorManager.unregisterListener(mListener);
mHandler.removeCallbacks(mMeasurementTimeout);
+ mMeasurementTimeoutIsActive = false;
+ mSensorManager.unregisterListener(mListener);
mMeasurementInProgress = false;
mPreviousGravityVector = mCurrentGravityVector;
mCurrentGravityVector = mRunningStats.getRunningAverage();
if (DEBUG) Slog.d(TAG, "getStationaryStatus() returned " + status);
if (status != RESULT_UNKNOWN) {
if (mWakeLock.isHeld()) {
- mWakeLock.release();
mHandler.removeCallbacks(mWakelockTimeout);
+ mWakelockTimeoutIsActive = false;
+ mWakeLock.release();
}
if (DEBUG) {
Slog.d(TAG, "Moved from STATE_ACTIVE to STATE_INACTIVE. status = " + status);
" milliseconds.");
Message msg = Message.obtain(mHandler, mSensorRestart);
mHandler.sendMessageDelayed(msg, ORIENTATION_MEASUREMENT_INTERVAL_MILLIS);
+ mSensorRestartIsActive = true;
}
}
return status;
}
if (status != RESULT_UNKNOWN) {
mHandler.removeCallbacks(mWakelockTimeout);
+ mWakelockTimeoutIsActive = false;
mCallback.onAnyMotionResult(status);
}
}
@Override
public void run() {
synchronized (mLock) {
- startOrientationMeasurementLocked();
+ if (mSensorRestartIsActive == true) {
+ mSensorRestartIsActive = false;
+ startOrientationMeasurementLocked();
+ }
}
}
};
public void run() {
int status = RESULT_UNKNOWN;
synchronized (mLock) {
- if (DEBUG) Slog.i(TAG, "mMeasurementTimeout. Failed to collect sufficient accel " +
- "data within " + ACCELEROMETER_DATA_TIMEOUT_MILLIS + " ms. Stopping " +
- "orientation measurement.");
- status = stopOrientationMeasurementLocked();
- }
- if (status != RESULT_UNKNOWN) {
- mHandler.removeCallbacks(mWakelockTimeout);
- mCallback.onAnyMotionResult(status);
+ if (mMeasurementTimeoutIsActive == true) {
+ mMeasurementTimeoutIsActive = false;
+ if (DEBUG) Slog.i(TAG, "mMeasurementTimeout. Failed to collect sufficient accel " +
+ "data within " + ACCELEROMETER_DATA_TIMEOUT_MILLIS + " ms. Stopping " +
+ "orientation measurement.");
+ status = stopOrientationMeasurementLocked();
+ if (status != RESULT_UNKNOWN) {
+ mHandler.removeCallbacks(mWakelockTimeout);
+ mWakelockTimeoutIsActive = false;
+ mCallback.onAnyMotionResult(status);
+ }
+ }
}
}
};
@Override
public void run() {
synchronized (mLock) {
- stop();
+ if (mWakelockTimeoutIsActive == true) {
+ mWakelockTimeoutIsActive = false;
+ stop();
+ }
}
}
};
final int installerUid;
final SessionParams params;
final long createdMillis;
+ final int defaultContainerGid;
/** Staging location where client data is written. */
final File stageDir;
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
+ // Cache package manager data without the lock held
+ final PackageInfo pkgInfo = mPm.getPackageInfo(
+ params.appPackageName, PackageManager.GET_SIGNATURES /*flags*/, userId);
+ final ApplicationInfo appInfo = mPm.getApplicationInfo(
+ params.appPackageName, 0, userId);
+
synchronized (mLock) {
if (msg.obj != null) {
mRemoteObserver = (IPackageInstallObserver2) msg.obj;
}
try {
- commitLocked();
+ commitLocked(pkgInfo, appInfo);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
} else {
mPermissionsAccepted = false;
}
+ final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
+ PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
+ defaultContainerGid = UserHandle.getSharedAppGid(uid);
}
public SessionInfo generateInfo() {
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
- private void commitLocked() throws PackageManagerException {
+ private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
+ throws PackageManagerException {
if (mDestroyed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
}
// Verify that stage looks sane with respect to existing application.
// This currently only ensures packageName, versionCode, and certificate
// consistency.
- validateInstallLocked();
+ validateInstallLocked(pkgInfo, appInfo);
Preconditions.checkNotNull(mPackageName);
Preconditions.checkNotNull(mSignatures);
* Note that upgrade compatibility is still performed by
* {@link PackageManagerService}.
*/
- private void validateInstallLocked() throws PackageManagerException {
+ private void validateInstallLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
+ throws PackageManagerException {
mPackageName = null;
mVersionCode = -1;
mSignatures = null;
if (removeSplitList.size() > 0) {
// validate split names marked for removal
- final int flags = mSignatures == null ? PackageManager.GET_SIGNATURES : 0;
- final PackageInfo pkg = mPm.getPackageInfo(params.appPackageName, flags, userId);
for (String splitName : removeSplitList) {
- if (!ArrayUtils.contains(pkg.splitNames, splitName)) {
+ if (!ArrayUtils.contains(pkgInfo.splitNames, splitName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Split not found: " + splitName);
}
// ensure we've got appropriate package name, version code and signatures
if (mPackageName == null) {
- mPackageName = pkg.packageName;
- mVersionCode = pkg.versionCode;
+ mPackageName = pkgInfo.packageName;
+ mVersionCode = pkgInfo.versionCode;
}
if (mSignatures == null) {
- mSignatures = pkg.signatures;
+ mSignatures = pkgInfo.signatures;
}
}
} else {
// Partial installs must be consistent with existing install
- final ApplicationInfo app = mPm.getApplicationInfo(mPackageName, 0, userId);
- if (app == null) {
+ if (appInfo == null) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK,
"Missing existing base package for " + mPackageName);
}
final PackageLite existing;
final ApkLite existingBase;
try {
- existing = PackageParser.parsePackageLite(new File(app.getCodePath()), 0);
- existingBase = PackageParser.parseApkLite(new File(app.getBaseCodePath()),
+ existing = PackageParser.parsePackageLite(new File(appInfo.getCodePath()), 0);
+ existingBase = PackageParser.parseApkLite(new File(appInfo.getBaseCodePath()),
PackageParser.PARSE_COLLECT_CERTIFICATES);
} catch (PackageParserException e) {
throw PackageManagerException.from(e);
// Inherit base if not overridden
if (mResolvedBaseFile == null) {
- mResolvedBaseFile = new File(app.getBaseCodePath());
+ mResolvedBaseFile = new File(appInfo.getBaseCodePath());
mResolvedInheritedFiles.add(mResolvedBaseFile);
}
}
// Inherit compiled oat directory.
- final File packageInstallDir = (new File(app.getBaseCodePath())).getParentFile();
+ final File packageInstallDir = (new File(appInfo.getBaseCodePath())).getParentFile();
mInheritedFilesBase = packageInstallDir;
final File oatDir = new File(packageInstallDir, "oat");
if (oatDir.exists()) {
}
}
- private void assertApkConsistent(String tag, ApkLite apk) throws PackageManagerException {
+ private void assertApkConsistent(String tag, ApkLite apk)
+ throws PackageManagerException {
if (!mPackageName.equals(apk.packageName)) {
throw new PackageManagerException(INSTALL_FAILED_INVALID_APK, tag + " package "
+ apk.packageName + " inconsistent with " + mPackageName);
"Failed to finalize container " + cid);
}
- final int uid = mPm.getPackageUid(PackageManagerService.DEFAULT_CONTAINER_PACKAGE,
- PackageManager.MATCH_SYSTEM_ONLY, UserHandle.USER_SYSTEM);
- final int gid = UserHandle.getSharedAppGid(uid);
- if (!PackageHelper.fixSdPermissions(cid, gid, null)) {
+ if (!PackageHelper.fixSdPermissions(cid, defaultContainerGid, null)) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to fix permissions on container " + cid);
}
for (i=0; i<N; i++) {
PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
PackageParser.PermissionGroup cur = mPermissionGroups.get(pg.info.name);
- if (cur == null) {
+ final String curPackageName = cur == null ? null : cur.info.packageName;
+ final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName);
+ if (cur == null || isPackageUpdate) {
mPermissionGroups.put(pg.info.name, pg);
if ((policyFlags&PackageParser.PARSE_CHATTY) != 0) {
if (r == null) {
} else {
r.append(' ');
}
+ if (isPackageUpdate) {
+ r.append("UPD:");
+ }
r.append(pg.info.name);
}
} else {
int CHECK_LAUNCHER_ACTIVITY = 12;
int IS_ACTIVITY_ENABLED = 13;
int PACKAGE_UPDATE_CHECK = 14;
-
- int COUNT = PACKAGE_UPDATE_CHECK + 1;
- }
+ int ASYNC_PRELOAD_USER_DELAY = 15;
+
+ int COUNT = ASYNC_PRELOAD_USER_DELAY + 1;
+ }
+
+ private static final String[] STAT_LABELS = {
+ "getHomeActivities()",
+ "Launcher permission check",
+ "getPackageInfo()",
+ "getPackageInfo(SIG)",
+ "getApplicationInfo",
+ "cleanupDanglingBitmaps",
+ "getActivity+metadata",
+ "getInstalledPackages",
+ "checkPackageChanges",
+ "getApplicationResources",
+ "resourceNameLookup",
+ "getLauncherActivity",
+ "checkLauncherActivity",
+ "isActivityEnabled",
+ "packageUpdateCheck",
+ "asyncPreloadUserDelay"
+ };
final Object mStatLock = new Object();
/** lifecycle event */
void handleUnlockUser(int userId) {
if (DEBUG) {
- Slog.d(TAG, "handleUnlockUser: user=" + userId);
+ Slog.d(TAG, "handleUnlockUser: user=" + userId);
}
synchronized (mLock) {
mUnlockedUsers.put(userId, true);
-
- // Preload the user's shortcuts.
- // Also see if the locale has changed.
- // Note as of nyc, the locale is per-user, so the locale shouldn't change
- // when the user is locked. However due to b/30119489 it still happens.
- getUserShortcutsLocked(userId).detectLocaleChange();
-
- checkPackageChanges(userId);
}
+
+ // Preload the user data.
+ // Note, we don't use mHandler here but instead just start a new thread.
+ // This is because mHandler (which uses com.android.internal.os.BackgroundThread) is very
+ // busy at this point and this could take hundreds of milliseconds, which would be too
+ // late since the launcher would already have started.
+ // So we just create a new thread. This code runs rarely, so we don't use a thread pool
+ // or anything.
+ final long start = injectElapsedRealtime();
+ injectRunOnNewThread(() -> {
+ synchronized (mLock) {
+ logDurationStat(Stats.ASYNC_PRELOAD_USER_DELAY, start);
+ getUserShortcutsLocked(userId);
+ }
+ });
}
/** lifecycle event */
userPackages = new ShortcutUser(this, userId);
}
mUsers.put(userId, userPackages);
+
+ // Also when a user's data is first accessed, scan all packages.
+ checkPackageChanges(userId);
}
return userPackages;
}
mHandler.post(r);
}
+ void injectRunOnNewThread(Runnable r) {
+ new Thread(r).start();
+ }
+
/**
* @throws IllegalArgumentException if {@code numShortcuts} is bigger than
* {@link #getMaxActivityShortcuts()}.
pw.println(" Stats:");
synchronized (mStatLock) {
- final String p = " ";
- dumpStatLS(pw, p, Stats.GET_DEFAULT_HOME, "getHomeActivities()");
- dumpStatLS(pw, p, Stats.LAUNCHER_PERMISSION_CHECK, "Launcher permission check");
-
- dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO, "getPackageInfo()");
- dumpStatLS(pw, p, Stats.GET_PACKAGE_INFO_WITH_SIG, "getPackageInfo(SIG)");
- dumpStatLS(pw, p, Stats.GET_APPLICATION_INFO, "getApplicationInfo");
- dumpStatLS(pw, p, Stats.CLEANUP_DANGLING_BITMAPS, "cleanupDanglingBitmaps");
- dumpStatLS(pw, p, Stats.GET_ACTIVITY_WITH_METADATA, "getActivity+metadata");
- dumpStatLS(pw, p, Stats.GET_INSTALLED_PACKAGES, "getInstalledPackages");
- dumpStatLS(pw, p, Stats.CHECK_PACKAGE_CHANGES, "checkPackageChanges");
- dumpStatLS(pw, p, Stats.GET_APPLICATION_RESOURCES, "getApplicationResources");
- dumpStatLS(pw, p, Stats.RESOURCE_NAME_LOOKUP, "resourceNameLookup");
- dumpStatLS(pw, p, Stats.GET_LAUNCHER_ACTIVITY, "getLauncherActivity");
- dumpStatLS(pw, p, Stats.CHECK_LAUNCHER_ACTIVITY, "checkLauncherActivity");
- dumpStatLS(pw, p, Stats.IS_ACTIVITY_ENABLED, "isActivityEnabled");
- dumpStatLS(pw, p, Stats.PACKAGE_UPDATE_CHECK, "packageUpdateCheck");
+ for (int i = 0; i < Stats.COUNT; i++) {
+ dumpStatLS(pw, " ", i);
+ }
}
pw.println();
return tobj.format("%Y-%m-%d %H:%M:%S");
}
- private void dumpStatLS(PrintWriter pw, String prefix, int statId, String label) {
+ private void dumpStatLS(PrintWriter pw, String prefix, int statId) {
pw.print(prefix);
final int count = mCountStats[statId];
final long dur = mDurationStats[statId];
pw.println(String.format("%s: count=%d, total=%dms, avg=%.1fms",
- label, count, dur,
+ STAT_LABELS[statId], count, dur,
(count == 0 ? 0 : ((double) dur) / count)));
}
}
final WindowState w = mTopFullscreenOpaqueWindowState;
+ if (w != mFocusedWindow) {
+ return false;
+ }
// We only enable seamless rotation if the top window has requested
// it and is in the fullscreen opaque state. Seamless rotation
}
result |= RELAYOUT_RES_SURFACE_CHANGED;
}
- final WindowSurfaceController surfaceController = winAnimator.mSurfaceController;
- if (viewVisibility == View.VISIBLE && surfaceController != null) {
+ if (viewVisibility == View.VISIBLE && winAnimator.hasSurface()) {
// We already told the client to go invisible, but the message may not be
// handled yet, or it might want to draw a last frame. If we already have a
// surface, let the client use that, but don't create new surface at this point.
- surfaceController.getSurface(outSurface);
+ winAnimator.mSurfaceController.getSurface(outSurface);
} else {
if (DEBUG_VISIBILITY) Slog.i(TAG_WM, "Releasing surface in: " + win);
private final State mStoppedState = new StoppedState();
private final State mStoppingState = new StoppingState();
private final State mStartedState = new StartedState();
+ private final State mRunningState = new RunningState();
private final String mTag;
private final Context mContext;
// Super simple StateMachine.
addState(mStoppedState);
addState(mStartedState);
+ addState(mRunningState, mStartedState);
addState(mStoppingState);
setInitialState(mStoppedState);
pw.decreaseIndent();
pw.println();
- pw.println("StateMachine dump:");
+ pw.println(mTag + " StateMachine dump:");
pw.increaseIndent();
mLocalLog.readOnlyLocalLog().dump(fd, pw, args);
pw.decreaseIndent();
// - IPv6 addresses
// - IPv6 routes
// - IPv6 DNS servers
+ //
+ // N.B.: this is fundamentally race-prone and should be fixed by
+ // changing NetlinkTracker from a hybrid edge/level model to an
+ // edge-only model, or by giving IpManager its own netlink socket(s)
+ // so as to track all required information directly.
LinkProperties netlinkLinkProperties = mNetlinkTracker.getLinkProperties();
newLp.setLinkAddresses(netlinkLinkProperties.getLinkAddresses());
for (RouteInfo route : netlinkLinkProperties.getRoutes()) {
return true;
}
+ private void stopAllIP() {
+ // We don't need to worry about routes, just addresses, because:
+ // - disableIpv6() will clear autoconf IPv6 routes as well, and
+ // - we don't get IPv4 routes from netlink
+ // so we neither react to nor need to wait for changes in either.
+
+ try {
+ mNwService.disableIpv6(mInterfaceName);
+ } catch (Exception e) {
+ Log.e(mTag, "Failed to disable IPv6" + e);
+ }
+
+ try {
+ mNwService.clearInterfaceAddresses(mInterfaceName);
+ } catch (Exception e) {
+ Log.e(mTag, "Failed to clear addresses " + e);
+ }
+ }
+
class StoppedState extends State {
@Override
public void enter() {
- try {
- mNwService.disableIpv6(mInterfaceName);
- mNwService.clearInterfaceAddresses(mInterfaceName);
- } catch (Exception e) {
- Log.e(mTag, "Failed to clear addresses or disable IPv6" + e);
- }
+ stopAllIP();
resetLinkProperties();
if (mStartTimeMillis > 0) {
}
class StartedState extends State {
- private boolean mDhcpActionInFlight;
-
@Override
public void enter() {
mStartTimeMillis = SystemClock.elapsedRealtime();
+ if (mConfiguration.mProvisioningTimeoutMs > 0) {
+ final long alarmTime = SystemClock.elapsedRealtime() +
+ mConfiguration.mProvisioningTimeoutMs;
+ mProvisioningTimeoutAlarm.schedule(alarmTime);
+ }
+
+ if (readyToProceed()) {
+ transitionTo(mRunningState);
+ } else {
+ // Clear all IPv4 and IPv6 before proceeding to RunningState.
+ // Clean up any leftover state from an abnormal exit from
+ // tethering or during an IpManager restart.
+ stopAllIP();
+ }
+ }
+
+ @Override
+ public void exit() {
+ mProvisioningTimeoutAlarm.cancel();
+ }
+
+ @Override
+ public boolean processMessage(Message msg) {
+ switch (msg.what) {
+ case CMD_STOP:
+ transitionTo(mStoppingState);
+ break;
+
+ case EVENT_NETLINK_LINKPROPERTIES_CHANGED:
+ handleLinkPropertiesUpdate(NO_CALLBACKS);
+ if (readyToProceed()) {
+ transitionTo(mRunningState);
+ }
+ break;
+
+ case EVENT_PROVISIONING_TIMEOUT:
+ handleProvisioningFailure();
+ break;
+
+ default:
+ // It's safe to process messages out of order because the
+ // only message that can both
+ // a) be received at this time and
+ // b) affect provisioning state
+ // is EVENT_NETLINK_LINKPROPERTIES_CHANGED (handled above).
+ deferMessage(msg);
+ }
+ return HANDLED;
+ }
+
+ boolean readyToProceed() {
+ return (!mLinkProperties.hasIPv4Address() &&
+ !mLinkProperties.hasGlobalIPv6Address());
+ }
+ }
+
+ class RunningState extends State {
+ private boolean mDhcpActionInFlight;
+
+ @Override
+ public void enter() {
mApfFilter = ApfFilter.maybeCreate(mConfiguration.mApfCapabilities, mNetworkInterface,
mCallback, mMulticastFiltering);
// TODO: investigate the effects of any multicast filtering racing/interfering with the
mCallback.setFallbackMulticastFilter(mMulticastFiltering);
}
- if (mConfiguration.mProvisioningTimeoutMs > 0) {
- final long alarmTime = SystemClock.elapsedRealtime() +
- mConfiguration.mProvisioningTimeoutMs;
- mProvisioningTimeoutAlarm.schedule(alarmTime);
- }
-
if (mConfiguration.mEnableIPv6) {
// TODO: Consider transitionTo(mStoppingState) if this fails.
startIPv6();
@Override
public void exit() {
- mProvisioningTimeoutAlarm.cancel();
stopDhcpAction();
if (mIpReachabilityMonitor != null) {
break;
}
- case EVENT_PROVISIONING_TIMEOUT:
- handleProvisioningFailure();
- break;
-
case EVENT_DHCPACTION_TIMEOUT:
stopDhcpAction();
break;
}
@Override
+ void injectRunOnNewThread(Runnable r) {
+ runOnHandler(r);
+ }
+
+ @Override
void injectEnforceCallingPermission(String permission, String message) {
if (!mCallerPermissions.contains(permission)) {
throw new SecurityException("Missing permission: " + permission);
});
}
+ protected void setPackageLastUpdateTime(String packageName, long value) {
+ updatePackageInfo(packageName, pi -> {
+ pi.lastUpdateTime = value;
+ });
+ }
+
protected void uninstallPackage(int userId, String packageName) {
if (ENABLE_DUMP) {
Log.v(TAG, "Unnstall package " + packageName + " / " + userId);
mInjectedPackages.remove(CALLING_PACKAGE_1);
mInjectedPackages.remove(CALLING_PACKAGE_3);
- mService.handleUnlockUser(USER_0);
+ mService.checkPackageChanges(USER_0);
assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
- assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0));
+ assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_0)); // ---------------
assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_10));
assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_10));
assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_3, "s1", USER_10));
assertFalse(bitmapDirectoryExists(CALLING_PACKAGE_2, USER_10));
assertTrue(bitmapDirectoryExists(CALLING_PACKAGE_3, USER_10));
- mService.handleUnlockUser(USER_10);
+ mService.checkPackageChanges(USER_10);
assertNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_1, "s1", USER_0));
assertNotNull(mService.getPackageShortcutForTest(CALLING_PACKAGE_2, "s1", USER_0));
updatePackageVersion(CALLING_PACKAGE_1, 1);
// Then send the broadcast, to only user-0.
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mService.mPackageMonitor.onReceive(getTestContext(),
genPackageUpdateIntent(CALLING_PACKAGE_1, USER_0));
waitOnMainThread();
mInjectedCurrentTimeMillis = START_TIME + 200;
mRunningUsers.put(USER_10, true);
+ mUnlockedUsers.put(USER_10, true);
reset(c0);
reset(c10);
+ setPackageLastUpdateTime(CALLING_PACKAGE_1, mInjectedCurrentTimeMillis);
mService.handleUnlockUser(USER_10);
+ mService.checkPackageChanges(USER_10);
waitOnMainThread();
// Then send the broadcast, to only user-0.
mService.mPackageMonitor.onReceive(getTestContext(),
genPackageUpdateIntent(CALLING_PACKAGE_2, USER_0));
- mService.handleUnlockUser(USER_10);
+ mService.checkPackageChanges(USER_10);
waitOnMainThread();
updatePackageVersion(CALLING_PACKAGE_3, 100);
// Then send the broadcast, to only user-0.
- mService.mPackageMonitor.onReceive(getTestContext(),
+ mService.mPackageMonitor.onReceive(getTestContext(),
genPackageUpdateIntent(CALLING_PACKAGE_3, USER_0));
- mService.handleUnlockUser(USER_10);
+ mService.checkPackageChanges(USER_10);
waitOnMainThread();
+ " user=" + userHandle);
}
+ ComponentName getCurAssistant(int userHandle) {
+ String curAssistant = Settings.Secure.getStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.ASSISTANT, userHandle);
+ if (TextUtils.isEmpty(curAssistant)) {
+ return null;
+ }
+ if (DEBUG) Slog.d(TAG, "getCurAssistant curAssistant=" + curAssistant
+ + " user=" + userHandle);
+ return ComponentName.unflattenFromString(curAssistant);
+ }
+
void resetCurAssistant(int userHandle) {
Settings.Secure.putStringForUser(mContext.getContentResolver(),
Settings.Secure.ASSISTANT, null, userHandle);
synchronized (VoiceInteractionManagerServiceStub.this) {
ComponentName curInteractor = getCurInteractor(userHandle);
ComponentName curRecognizer = getCurRecognizer(userHandle);
+ ComponentName curAssistant = getCurAssistant(userHandle);
if (curRecognizer == null) {
// Could a new recognizer appear when we don't have one pre-installed?
if (anyPackagesAppearing()) {
// the default config.
setCurInteractor(null, userHandle);
setCurRecognizer(null, userHandle);
+ resetCurAssistant(userHandle);
initForUser(userHandle);
return;
}
return;
}
+ if (curAssistant != null) {
+ int change = isPackageDisappearing(curAssistant.getPackageName());
+ if (change == PACKAGE_PERMANENT_CHANGE) {
+ // If the currently set assistant is being removed, then we should
+ // reset back to the default state (which is probably that we prefer
+ // to have the default full voice interactor enabled).
+ setCurInteractor(null, userHandle);
+ setCurRecognizer(null, userHandle);
+ resetCurAssistant(userHandle);
+ initForUser(userHandle);
+ return;
+ }
+ }
+
// There is no interactor, so just deal with a simple recognizer.
int change = isPackageDisappearing(curRecognizer.getPackageName());
if (change == PACKAGE_PERMANENT_CHANGE
all_sequences = set()
all_sequences.update(_emoji_variation_sequences)
+ # add zwj sequences not in the current emoji-zwj-sequences.txt
+ adjusted_emoji_zwj_sequences = dict(_emoji_zwj_sequences)
+ adjusted_emoji_zwj_sequences.update(_emoji_zwj_sequences)
+ # single parent families
+ additional_emoji_zwj = (
+ (0x1F468, 0x200D, 0x1F466),
+ (0x1F468, 0x200D, 0x1F467),
+ (0x1F468, 0x200D, 0x1F466, 0x200D, 0x1F466),
+ (0x1F468, 0x200D, 0x1F467, 0x200D, 0x1F466),
+ (0x1F468, 0x200D, 0x1F467, 0x200D, 0x1F467),
+ (0x1F469, 0x200D, 0x1F466),
+ (0x1F469, 0x200D, 0x1F467),
+ (0x1F469, 0x200D, 0x1F466, 0x200D, 0x1F466),
+ (0x1F469, 0x200D, 0x1F467, 0x200D, 0x1F466),
+ (0x1F469, 0x200D, 0x1F467, 0x200D, 0x1F467),
+ )
+ # sequences formed from man and woman and optional fitzpatrick modifier
+ modified_extensions = (
+ 0x2696,
+ 0x2708,
+ 0x1F3A8,
+ 0x1F680,
+ 0x1F692,
+ )
+ for seq in additional_emoji_zwj:
+ adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
+ for ext in modified_extensions:
+ for base in (0x1F468, 0x1F469):
+ seq = (base, 0x200D, ext)
+ adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
+ for modifier in range(0x1F3FB, 0x1F400):
+ seq = (base, modifier, 0x200D, ext)
+ adjusted_emoji_zwj_sequences[seq] = 'Emoji_ZWJ_Sequence'
+
for sequence in _emoji_sequences.keys():
sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
all_sequences.add(sequence)
sequence_pieces.update(sequence)
- for sequence in _emoji_zwj_sequences.keys():
+ for sequence in adjusted_emoji_zwj_sequences.keys():
sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
all_sequences.add(sequence)
sequence_pieces.update(sequence)