/** @hide */
oneway interface ITransientNotification {
- void show();
+ void show(IBinder windowToken);
void hide();
}
* If the calling launcher application contains pinned shortcuts, they will still work,
* even though the caller no longer has the shortcut host permission.
*
- * @throws IllegalStateException when the user is locked.
+ * <p>Returns {@code false} when the user is locked.
*
* @see ShortcutManager
*/
* <p>Callers must be allowed to access the shortcut information, as defined in {@link
* #hasShortcutHostPermission()}.
*
+ * <p>Returns am empty list when the user is locked, or when the {@code user} user
+ * is locked or not running.
+ *
* @param query result includes shortcuts matching this query.
* @param user The UserHandle of the profile.
*
* @return the IDs of {@link ShortcutInfo}s that match the query.
- * @throws IllegalStateException when the user is locked, or when the {@code user} user
- * is locked or not running.
*
* @see ShortcutManager
*/
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
+ * <p>Call will be ignored when the user is locked, or when the {@code user} user
+ * is locked or not running.
+ *
* @param packageName The target package name.
* @param shortcutIds The IDs of the shortcut to be pinned.
* @param user The UserHandle of the profile.
- * @throws IllegalStateException when the user is locked, or when the {@code user} user
- * is locked or not running.
*
* @see ShortcutManager
*/
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
+ * <p>Returns {@code null} when the user is locked, or when the user owning the shortcut
+ * is locked or not running.
+ *
* @param density The preferred density of the icon, zero for default density. Use
* density DPI values from {@link DisplayMetrics}.
*
* @return The drawable associated with the shortcut.
- * @throws IllegalStateException when the user is locked, or when the {@code user} user
- * is locked or not running.
*
* @see ShortcutManager
* @see #getShortcutBadgedIconDrawable(ShortcutInfo, int)
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
+ * <p>Returns {@code 0} when the user is locked, or when the user owning the shortcut
+ * is locked or not running.
+ *
* @param density Optional density for the icon, or 0 to use the default density. Use
* @return A badged icon for the shortcut.
- * @throws IllegalStateException when the user is locked, or when the {@code user} user
- * is locked or not running.
*
* @see ShortcutManager
* @see #getShortcutIconDrawable(ShortcutInfo, int)
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
+ * <p>Throws {@link android.content.ActivityNotFoundException}
+ * when the user is locked, or when the {@code user} user
+ * is locked or not running.
+ *
* @param packageName The target shortcut package name.
* @param shortcutId The target shortcut ID.
* @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity.
* @param user The UserHandle of the profile.
- * @throws IllegalStateException when the user is locked, or when the {@code user} user
- * is locked or not running.
*
* @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
* the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
* <p>The calling launcher application must be allowed to access the shortcut information,
* as defined in {@link #hasShortcutHostPermission()}.
*
+ * <p>Throws {@link android.content.ActivityNotFoundException}
+ * when the user is locked, or when the user owning the shortcut
+ * is locked or not running.
+ *
* @param shortcut The target shortcut.
* @param sourceBounds The Rect containing the source bounds of the clicked icon.
* @param startActivityOptions Options to pass to startActivity.
- * @throws IllegalStateException when the user is locked, or when the {@code user} user
- * is locked or not running.
*
* @throws android.content.ActivityNotFoundException failed to start shortcut. (e.g.
* the shortcut no longer exists, is disabled, the intent receiver activity doesn't exist, etc)
mSession = getWindowSession();
mLayout.token = getWindowToken();
mLayout.setTitle("SurfaceView - " + getViewRootImpl().getTitle());
+ mLayout.packageName = mContext.getOpPackageName();
mViewVisibility = getVisibility() == VISIBLE;
if (!mGlobalListenersAdded) {
public CharSequence accessibilityTitle;
/**
- * Sets a timeout in milliseconds before which the window will be removed
+ * Sets a timeout in milliseconds before which the window will be hidden
* by the window manager. Useful for transient notifications like toasts
* so we don't have to rely on client cooperation to ensure the window
- * is removed. Must be specified at window creation time.
+ * is hidden. Must be specified at window creation time. Note that apps
+ * are not prepared to handle their windows being removed without their
+ * explicit request and may try to interact with the removed window
+ * resulting in undefined behavior and crashes. Therefore, we do hide
+ * such windows to prevent them from overlaying other apps.
*
* @hide
*/
- public long removeTimeoutMilliseconds = -1;
+ public long hideTimeoutMilliseconds = -1;
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
out.writeInt(needsMenuKey);
out.writeInt(accessibilityIdOfAnchor);
TextUtils.writeToParcel(accessibilityTitle, out, parcelableFlags);
- out.writeLong(removeTimeoutMilliseconds);
+ out.writeLong(hideTimeoutMilliseconds);
}
public static final Parcelable.Creator<LayoutParams> CREATOR
needsMenuKey = in.readInt();
accessibilityIdOfAnchor = in.readInt();
accessibilityTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- removeTimeoutMilliseconds = in.readLong();
+ hideTimeoutMilliseconds = in.readLong();
}
@SuppressWarnings({"PointlessBitwiseExpression"})
}
// This can't change, it's only set at window creation time.
- removeTimeoutMilliseconds = o.removeTimeoutMilliseconds;
+ hideTimeoutMilliseconds = o.hideTimeoutMilliseconds;
return changes;
}
}
// A touch inside a star fill up to that fractional area (slightly more
- // than 1 so boundaries round up).
- mTouchProgressOffset = 1.1f;
+ // than 0.5 so boundaries round up).
+ mTouchProgressOffset = 0.6f;
}
public RatingBar(Context context, AttributeSet attrs) {
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
}
private static class TN extends ITransientNotification.Stub {
- final Runnable mShow = new Runnable() {
- @Override
- public void run() {
- handleShow();
- }
- };
-
final Runnable mHide = new Runnable() {
@Override
public void run() {
};
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
- final Handler mHandler = new Handler();
+ final Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ IBinder token = (IBinder) msg.obj;
+ handleShow(token);
+ }
+ };
int mGravity;
int mX, mY;
* schedule handleShow into the right thread
*/
@Override
- public void show() {
+ public void show(IBinder windowToken) {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
- mHandler.post(mShow);
+ mHandler.obtainMessage(0, windowToken).sendToTarget();
}
/**
mHandler.post(mHide);
}
- public void handleShow() {
+ public void handleShow(IBinder windowToken) {
if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ " mNextView=" + mNextView);
if (mView != mNextView) {
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
mParams.packageName = packageName;
- mParams.removeTimeoutMilliseconds = mDuration ==
+ mParams.hideTimeoutMilliseconds = mDuration ==
Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
+ mParams.token = windowToken;
if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:color="?android:colorAccent" android:state_checked="true" />
- <item android:color="?android:colorButtonNormal" android:state_checked="false" />
+ <item android:color="?android:colorButtonNormal" android:state_enabled="false" />
+ <item android:color="?android:colorControlActivated" android:state_checked="true" />
+ <item android:color="?android:colorButtonNormal" />
</selector>
\ No newline at end of file
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>En este documento</h2>
<ol>
<li><a href="#run">Solicitar acceso para ejecutar durante el inicio directo</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>En este documento</h2>
<ol>
<li><a href="#accessing">Acceder a un directorio de almacenamiento externo</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>En este documento</h2>
<ol>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>En este documento</h2>
<ol>
<li><a href="#supporting">Indicar la compatibilidad para la grabación</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Dalam dokumen ini</h2>
<ol>
<li><a href="#run">Meminta Akses untuk Berjalan Selama Direct Boot</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Dalam dokumen ini</h2>
<ol>
<li><a href="#accessing">Mengakses Direktori Penyimpanan Eksternal</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Dalam dokumen ini</h2>
<ol>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Dalam dokumen ini</h2>
<ol>
<li><a href="#supporting">Menunjukkan Dukungan untuk Perekaman</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>このドキュメントの内容</h2>
<ol>
<li><a href="#run">ダイレクト ブート中に実行するためのアクセスを要求する</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>このドキュメントの内容</h2>
<ol>
<li><a href="#accessing">外部ストレージのディレクトリへのアクセス</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>このドキュメントの内容</h2>
<ol>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>このドキュメントの内容</h2>
<ol>
<li><a href="#supporting">録画のサポートを示す</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>이 문서의 내용</h2>
<ol>
<li><a href="#run">직접 부팅 시 실행하기 위한 액세스 요청</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>이 문서의 내용</h2>
<ol>
<li><a href="#accessing">외부 저장소 디렉터리 액세스</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>이 문서의 내용</h2>
<ol>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>이 문서의 내용</h2>
<ol>
<li><a href="#supporting">녹화 지원 나타내기</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Neste documento</h2>
<ol>
<li><a href="#run">Solicitar acesso para executar durante a inicialização direta</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Neste documento</h2>
<ol>
<li><a href="#accessing">Acessar um diretório de armazenamento externo</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Neste documento</h2>
<ol>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Neste documento</h2>
<ol>
<li><a href="#supporting">Indicar suporte para gravação</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Содержание документа</h2>
<ol>
<li><a href="#run">Запрос доступа для запуска в режиме Direct Boot</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Содержание документа</h2>
<ol>
<li><a href="#accessing">Доступ к каталогу во внешнем хранилище</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Содержание документа</h2>
<ol>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Содержание документа</h2>
<ol>
<li><a href="#supporting">Указание на поддержку записи</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Trong tài liệu này</h2>
<ol>
<li><a href="#run">Yêu cầu Truy cập để Chạy trong quá trình Khởi động Trực tiếp</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Trong tài liệu này</h2>
<ol>
<li><a href="#accessing">Truy cập một Thư mục lưu trữ bên ngoài</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Trong tài liệu này</h2>
<ol>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>Trong tài liệu này</h2>
<ol>
<li><a href="#supporting">Chỉ báo Hỗ trợ ghi lại</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>本文内容</h2>
<ol>
<li><a href="#run">请求在直接启动时运行</a></li>
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>本文内容</h2>
<ol>
<li><a href="#accessing">访问外部存储目录</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>本文内容</h2>
<ol>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>本文内容</h2>
<ol>
<li><a href="#supporting">指示支持录制</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>此文件內容</h2>
<ol>
<li><a href="#run">要求直接開機期間的執行權限</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>此文件內容</h2>
<ol>
<li><a href="#accessing">存取外部儲存空間目錄</a></li>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>此文件內容</h2>
<ol>
@jd:body
-<div id="qv-wrapper">
-<div id="qv">
+<div id="tb-wrapper">
+<div id="tb">
<h2>此文件內容</h2>
<ol>
<li><a href="#supporting">指出錄製支援</a></li>
</div>
-<p style="clear:both"><em>Data collected during a 7-day period ending on July 11, 2016.
+<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2016.
<br/>Any versions with less than 0.1% distribution are not shown.</em>
</p>
</div>
-<p style="clear:both"><em>Data collected during a 7-day period ending on July 11, 2016.
+<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2016.
<br/>Any screen configurations with less than 0.1% distribution are not shown.</em></p>
<img alt="" style="float:right"
-src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0%7CGL%203.1&chf=bg%2Cs%2C00000000&chd=t%3A47.5%2C41.9%2C10.6&chco=c4df9b%2C6fad0c&cht=p&chs=400x250">
+src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0%7CGL%203.1&chf=bg%2Cs%2C00000000&chd=t%3A46.0%2C42.6%2C11.4&chco=c4df9b%2C6fad0c&cht=p&chs=400x250">
<p>To declare which version of OpenGL ES your application requires, you should use the {@code
android:glEsVersion} attribute of the <a
</tr>
<tr>
<td>2.0</td>
-<td>47.5%</td>
+<td>46.0%</td>
</tr>
<tr>
<td>3.0</td>
-<td>41.9%</td>
+<td>42.6%</td>
</tr>
<tr>
<td>3.1</td>
-<td>10.6%</td>
+<td>11.4%</td>
</tr>
</table>
-<p style="clear:both"><em>Data collected during a 7-day period ending on July 11, 2016</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on August 1, 2016</em></p>
"Large": {
"hdpi": "0.5",
"ldpi": "0.2",
- "mdpi": "4.4",
+ "mdpi": "4.3",
"tvdpi": "2.1",
"xhdpi": "0.5"
},
"Normal": {
- "hdpi": "40.9",
- "mdpi": "4.1",
+ "hdpi": "40.0",
+ "mdpi": "3.8",
"tvdpi": "0.1",
- "xhdpi": "26.3",
- "xxhdpi": "15.1"
+ "xhdpi": "27.3",
+ "xxhdpi": "15.5"
},
"Small": {
- "ldpi": "1.9"
+ "ldpi": "1.8"
},
"Xlarge": {
"hdpi": "0.3",
"xhdpi": "0.7"
}
},
- "densitychart": "//chart.googleapis.com/chart?chco=c4df9b%2C6fad0c&chd=t%3A2.1%2C11.4%2C2.2%2C41.7%2C27.5%2C15.1&chf=bg%2Cs%2C00000000&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chs=400x250&cht=p",
- "layoutchart": "//chart.googleapis.com/chart?chco=c4df9b%2C6fad0c&chd=t%3A3.9%2C7.7%2C86.5%2C1.9&chf=bg%2Cs%2C00000000&chl=Xlarge%7CLarge%7CNormal%7CSmall&chs=400x250&cht=p"
+ "densitychart": "//chart.googleapis.com/chart?chd=t%3A2.0%2C11.0%2C2.2%2C40.8%2C28.5%2C15.5&chf=bg%2Cs%2C00000000&chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&cht=p&chs=400x250&chco=c4df9b%2C6fad0c",
+ "layoutchart": "//chart.googleapis.com/chart?chd=t%3A3.9%2C7.6%2C86.7%2C1.8&chf=bg%2Cs%2C00000000&chl=Xlarge%7CLarge%7CNormal%7CSmall&cht=p&chs=400x250&chco=c4df9b%2C6fad0c"
}
];
var VERSION_DATA =
[
{
- "chart": "//chart.googleapis.com/chart?chco=c4df9b%2C6fad0c&chd=t%3A0.1%2C1.9%2C1.7%2C17.8%2C30.2%2C35.1%2C13.3&chf=bg%2Cs%2C00000000&chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat%7CLollipop%7CMarshmallow&chs=500x250&cht=p",
+ "chart": "//chart.googleapis.com/chart?chd=t%3A0.1%2C1.7%2C1.6%2C16.7%2C29.2%2C35.5%2C15.2&chf=bg%2Cs%2C00000000&chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat%7CLollipop%7CMarshmallow&cht=p&chs=500x250&chco=c4df9b%2C6fad0c",
"data": [
{
"api": 8,
{
"api": 10,
"name": "Gingerbread",
- "perc": "1.9"
+ "perc": "1.7"
},
{
"api": 15,
"name": "Ice Cream Sandwich",
- "perc": "1.7"
+ "perc": "1.6"
},
{
"api": 16,
"name": "Jelly Bean",
- "perc": "6.4"
+ "perc": "6.0"
},
{
"api": 17,
"name": "Jelly Bean",
- "perc": "8.8"
+ "perc": "8.3"
},
{
"api": 18,
"name": "Jelly Bean",
- "perc": "2.6"
+ "perc": "2.4"
},
{
"api": 19,
"name": "KitKat",
- "perc": "30.1"
+ "perc": "29.2"
},
{
"api": 21,
"name": "Lollipop",
- "perc": "14.3"
+ "perc": "14.1"
},
{
"api": 22,
"name": "Lollipop",
- "perc": "20.8"
+ "perc": "21.4"
},
{
"api": 23,
"name": "Marshmallow",
- "perc": "13.3"
+ "perc": "15.2"
}
]
}
import android.app.NotificationManager;
import android.app.NotificationManager.Policy;
import android.app.PendingIntent;
-import android.app.RemoteInput;
import android.app.StatusBarManager;
import android.app.backup.BackupManager;
import android.app.usage.UsageEvents;
import android.os.IInterface;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcelable;
import android.os.Process;
import android.os.RemoteException;
import android.os.SystemClock;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
+import android.view.WindowManager;
+import android.view.WindowManagerInternal;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.widget.Toast;
import com.android.server.lights.Light;
import com.android.server.lights.LightsManager;
import com.android.server.notification.ManagedServices.ManagedServiceInfo;
+import com.android.server.policy.PhoneWindowManager;
import com.android.server.statusbar.StatusBarManagerInternal;
import com.android.server.vr.VrManagerInternal;
import com.android.server.notification.ManagedServices.UserProfiles;
private static final int MESSAGE_RECONSIDER_RANKING = 1000;
private static final int MESSAGE_RANKING_SORT = 1001;
- static final int LONG_DELAY = 3500; // 3.5 seconds
+ static final int LONG_DELAY = PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
static final int SHORT_DELAY = 2000; // 2 seconds
static final long[] DEFAULT_VIBRATE_PATTERN = {0, 250, 250, 250};
@Nullable StatusBarManagerInternal mStatusBar;
Vibrator mVibrator;
private VrManagerInternal mVrManagerInternal;
+ private WindowManagerInternal mWindowManagerInternal;
final IBinder mForegroundToken = new Binder();
private Handler mHandler;
final String pkg;
final ITransientNotification callback;
int duration;
+ Binder token;
- ToastRecord(int pid, String pkg, ITransientNotification callback, int duration)
- {
+ ToastRecord(int pid, String pkg, ITransientNotification callback, int duration,
+ Binder token) {
this.pid = pid;
this.pkg = pkg;
this.callback = callback;
this.duration = duration;
+ this.token = token;
}
void update(int duration) {
mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
mAudioManagerInternal = getLocalService(AudioManagerInternal.class);
mVrManagerInternal = getLocalService(VrManagerInternal.class);
+ mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class);
mZenModeHelper.onSystemReady();
} else if (phase == SystemService.PHASE_THIRD_PARTY_APPS_CAN_START) {
// This observer will force an update when observe is called, causing us to
}
}
- record = new ToastRecord(callingPid, pkg, callback, duration);
+ Binder token = new Binder();
+ mWindowManagerInternal.addWindowToken(token,
+ WindowManager.LayoutParams.TYPE_TOAST);
+ record = new ToastRecord(callingPid, pkg, callback, duration, token);
mToastQueue.add(record);
index = mToastQueue.size() - 1;
- keepProcessAliveLocked(callingPid);
+ keepProcessAliveIfNeededLocked(callingPid);
}
// If it's at index 0, it's the current toast. It doesn't matter if it's
// new or just been updated. Call back and tell it to show itself.
while (record != null) {
if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
try {
- record.callback.show();
+ record.callback.show(record.token);
scheduleTimeoutLocked(record);
return;
} catch (RemoteException e) {
if (index >= 0) {
mToastQueue.remove(index);
}
- keepProcessAliveLocked(record.pid);
+ keepProcessAliveIfNeededLocked(record.pid);
if (mToastQueue.size() > 0) {
record = mToastQueue.get(0);
} else {
// don't worry about this, we're about to remove it from
// the list anyway
}
- mToastQueue.remove(index);
- keepProcessAliveLocked(record.pid);
+
+ ToastRecord lastToast = mToastQueue.remove(index);
+ mWindowManagerInternal.removeWindowToken(lastToast.token, true);
+
+ keepProcessAliveIfNeededLocked(record.pid);
if (mToastQueue.size() > 0) {
// Show the next one. If the callback fails, this will remove
// it from the list, so don't assume that the list hasn't changed
}
// lock on mToastQueue
- void keepProcessAliveLocked(int pid)
+ void keepProcessAliveIfNeededLocked(int pid)
{
int toastCount = 0; // toasts from this pid
ArrayList<ToastRecord> list = mToastQueue;
@Nullable ComponentName componentName,
int queryFlags, int userId) {
final ArrayList<ShortcutInfo> ret = new ArrayList<>();
-
- throwIfUserLocked(userId);
- throwIfUserLocked(launcherUserId);
+ if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
+ return ret;
+ }
final boolean cloneKeyFieldOnly =
((queryFlags & ShortcutQuery.FLAG_GET_KEY_FIELDS_ONLY) != 0);
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
- throwIfUserLocked(userId);
- throwIfUserLocked(launcherUserId);
+ if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
+ return false;
+ }
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId");
- throwIfUserLocked(userId);
- throwIfUserLocked(launcherUserId);
+ if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
+ return null;
+ }
final ShortcutPackage p = getUserShortcutsLocked(userId)
.getPackageShortcutsIfExists(packageName);
Preconditions.checkStringNotEmpty(packageName, "packageName");
Preconditions.checkNotNull(shortcutIds, "shortcutIds");
- throwIfUserLocked(userId);
- throwIfUserLocked(launcherUserId);
+ if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
+ return;
+ }
synchronized (mLock) {
final ShortcutLauncher launcher =
Preconditions.checkStringNotEmpty(packageName, "packageName can't be empty");
Preconditions.checkStringNotEmpty(shortcutId, "shortcutId can't be empty");
- throwIfUserLocked(userId);
- throwIfUserLocked(launcherUserId);
+ if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
+ return null;
+ }
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId");
- throwIfUserLocked(userId);
- throwIfUserLocked(launcherUserId);
+ if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
+ return 0;
+ }
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
Preconditions.checkNotNull(packageName, "packageName");
Preconditions.checkNotNull(shortcutId, "shortcutId");
- throwIfUserLocked(userId);
- throwIfUserLocked(launcherUserId);
+ if (!isUserUnlocked(userId) || !isUserUnlocked(launcherUserId)) {
+ return null;
+ }
synchronized (mLock) {
getLauncherShortcutsLocked(callingPackage, userId, launcherUserId)
@Override
public boolean hasShortcutHostPermission(int launcherUserId,
@NonNull String callingPackage) {
- throwIfUserLocked(launcherUserId);
+ if (!isUserUnlocked(launcherUserId)) {
+ return false;
+ }
return ShortcutService.this.hasShortcutHostPermission(callingPackage, launcherUserId);
}
}
/** Amount of time (in milliseconds) to wait for windows drawn before powering on. */
static final int WAITING_FOR_DRAWN_TIMEOUT = 1000;
+ /** Amount of time (in milliseconds) a toast window can be shown. */
+ public static final int TOAST_WINDOW_TIMEOUT = 3500; // 3.5 seconds
+
/**
* Lock protecting internal state. Must not call out into window
* manager with lock held. (This lock will be acquired in places
attrs.privateFlags &= ~WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
}
break;
+
+ case TYPE_TOAST:
+ // While apps should use the dedicated toast APIs to add such windows
+ // it possible legacy apps to add the window directly. Therefore, we
+ // make windows added directly by the app behave as a toast as much
+ // as possible in terms of timeout and animation.
+ if (attrs.hideTimeoutMilliseconds < 0
+ || attrs.hideTimeoutMilliseconds > TOAST_WINDOW_TIMEOUT) {
+ attrs.hideTimeoutMilliseconds = TOAST_WINDOW_TIMEOUT;
+ }
+ attrs.windowAnimations = com.android.internal.R.style.Animation_Toast;
+ break;
}
if (attrs.type != TYPE_STATUS_BAR) {
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import static android.view.WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION;
import static android.view.WindowManager.LayoutParams.TYPE_QS_DIALOG;
import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR;
+import static android.view.WindowManager.LayoutParams.TYPE_TOAST;
import static android.view.WindowManager.LayoutParams.TYPE_VOICE_INTERACTION;
import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
import static android.view.WindowManagerGlobal.RELAYOUT_DEFER_SURFACE_DESTROY;
boolean reportNewConfig = false;
WindowState attachedWindow = null;
long origId;
+ final int callingUid = Binder.getCallingUid();
final int type = attrs.type;
synchronized(mWindowMap) {
boolean addToken = false;
WindowToken token = mTokenMap.get(attrs.token);
AppWindowToken atoken = null;
+ boolean addToastWindowRequiresToken = false;
+
if (token == null) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
Slog.w(TAG_WM, "Attempted to add application window with unknown token "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
+ if (type == TYPE_TOAST) {
+ // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
+ if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,
+ attachedWindow)) {
+ Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "
+ + attrs.token + ". Aborting.");
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
+ }
+ }
token = new WindowToken(this, attrs.token, -1, false);
addToken = true;
} else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
+ } else if (type == TYPE_TOAST) {
+ // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
+ addToastWindowRequiresToken = doesAddToastWindowRequireToken(attrs.packageName,
+ callingUid, attachedWindow);
+ if (addToastWindowRequiresToken && token.windowType != TYPE_TOAST) {
+ Slog.w(TAG_WM, "Attempted to add a toast window with bad token "
+ + attrs.token + ". Aborting.");
+ return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
+ }
} else if (type == TYPE_QS_DIALOG) {
if (token.windowType != TYPE_QS_DIALOG) {
Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "
win.openInputChannel(outInputChannel);
}
+ // If adding a toast requires a token for this app we always schedule hiding
+ // toast windows to make sure they don't stick around longer then necessary.
+ // We hide instead of remove such windows as apps aren't prepared to handle
+ // windows being removed under them.
+ // If the app is older it can add toasts without a token and hence overlay
+ // other apps. To be maximally compatible with these apps we will hide the
+ // window after the toast timeout only if the focused window is from another
+ // UID, otherwise we allow unlimited duration. When a UID looses focus we
+ // schedule hiding all of its toast windows.
+ if (type == TYPE_TOAST) {
+ if (!canAddToastWindowForUid(getDefaultDisplayContentLocked(), callingUid)) {
+ Slog.w(TAG_WM, "Adding more than one toast window for UID at a time.");
+ return WindowManagerGlobal.ADD_DUPLICATE_ADD;
+ }
+ // Make sure this happens before we moved focus as one can make the
+ // toast focusable to force it not being hidden after the timeout.
+ // Focusable toasts are always timed out to prevent a focused app to
+ // show a focusable toasts while it has focus which will be kept on
+ // the screen after the activity goes away.
+ if (addToastWindowRequiresToken
+ || (attrs.flags & LayoutParams.FLAG_NOT_FOCUSABLE) == 0
+ || mCurrentFocus == null
+ || mCurrentFocus.mOwnerUid != callingUid) {
+ mH.sendMessageDelayed(
+ mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
+ win.mAttrs.hideTimeoutMilliseconds);
+ }
+ }
+
// From now on, no exceptions or errors allowed!
res = WindowManagerGlobal.ADD_OKAY;
if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {
reportNewConfig = true;
}
- if (attrs.removeTimeoutMilliseconds > 0) {
- mH.sendMessageDelayed(
- mH.obtainMessage(H.WINDOW_REMOVE_TIMEOUT, win),
- attrs.removeTimeoutMilliseconds);
- }
}
if (reportNewConfig) {
return res;
}
+ private boolean canAddToastWindowForUid(DisplayContent displayContent, int uid) {
+ // We allow one toast window per UID being shown at a time.
+ WindowList windows = displayContent.getWindowList();
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState window = windows.get(i);
+ if (window.mAttrs.type == TYPE_TOAST && window.mOwnerUid == uid
+ && !window.mPermanentlyHidden) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private boolean doesAddToastWindowRequireToken(String packageName, int callingUid,
+ WindowState attachedWindow) {
+ // Try using the target SDK of the root window
+ if (attachedWindow != null) {
+ WindowState currentWindow = attachedWindow;
+ while (currentWindow != null) {
+ if (currentWindow.mAppToken != null
+ && currentWindow.mAppToken.targetSdk > Build.VERSION_CODES.N_MR1) {
+ return true;
+ }
+ currentWindow = currentWindow.mAttachedWindow;
+ }
+ } else {
+ // Otherwise, look at the package
+ try {
+ ApplicationInfo appInfo = mContext.getPackageManager()
+ .getApplicationInfoAsUser(packageName, 0,
+ UserHandle.getUserId(callingUid));
+ if (appInfo.uid != callingUid) {
+ throw new SecurityException("Package " + packageName + " not in UID "
+ + callingUid);
+ }
+ if (appInfo.targetSdkVersion > Build.VERSION_CODES.N_MR1) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ /* ignore */
+ }
+ }
+ return false;
+ }
+
+ private void scheduleToastWindowsTimeoutIfNeededLocked(WindowState oldFocus,
+ WindowState newFocus) {
+ if (oldFocus == null || (newFocus != null && newFocus.mOwnerUid == oldFocus.mOwnerUid)) {
+ return;
+ }
+ final int lostFocusUid = oldFocus.mOwnerUid;
+ DisplayContent displayContent = oldFocus.getDisplayContent();
+ WindowList windows = displayContent.getWindowList();
+ final int windowCount = windows.size();
+ for (int i = 0; i < windowCount; i++) {
+ WindowState window = windows.get(i);
+ if (window.mAttrs.type == TYPE_TOAST && window.mOwnerUid == lostFocusUid) {
+ if (!mH.hasMessages(H.WINDOW_HIDE_TIMEOUT, window)) {
+ mH.sendMessageDelayed(
+ mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, window),
+ window.mAttrs.hideTimeoutMilliseconds);
+ }
+ }
+ }
+ }
+
/**
* Returns true if we're done setting up any transitions.
*/
public static final int NOTIFY_APP_TRANSITION_FINISHED = 49;
public static final int NOTIFY_STARTING_WINDOW_DRAWN = 50;
public static final int UPDATE_ANIMATION_SCALE = 51;
- public static final int WINDOW_REMOVE_TIMEOUT = 52;
+ public static final int WINDOW_HIDE_TIMEOUT = 52;
public static final int NOTIFY_DOCKED_STACK_MINIMIZED_CHANGED = 53;
public static final int SEAMLESS_ROTATION_TIMEOUT = 54;
mAmInternal.notifyStartingWindowDrawn();
}
break;
- case WINDOW_REMOVE_TIMEOUT: {
+ case WINDOW_HIDE_TIMEOUT: {
final WindowState window = (WindowState) msg.obj;
synchronized(mWindowMap) {
// TODO: This is all about fixing b/21693547
// running under debugger) to crash (b/29105388). The windows will
// eventually be removed when the client process finishes.
// The best we can do for now is remove the FLAG_KEEP_SCREEN_ON
- // and prevent the symptoms of b/21693547.
+ // and prevent the symptoms of b/21693547. Since apps don't
+ // support windows being removed under them we hide the window
+ // and it will be removed when the app dies.
window.mAttrs.flags &= ~FLAG_KEEP_SCREEN_ON;
+ window.markPermanentlyHiddenLw();
window.setDisplayLayoutNeeded();
mWindowPlacerLocked.performSurfacePlacement();
}
adjustForImeIfNeeded(displayContent);
+ // We may need to schedule some toast windows to be removed. The
+ // toasts for an app that does not have input focus are removed
+ // within a timeout to prevent apps to redress other apps' UI.
+ scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
+
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
return true;
}
boolean mPolicyVisibility = true;
boolean mPolicyVisibilityAfterAnim = true;
boolean mAppOpVisibility = true;
+ boolean mPermanentlyHidden;
boolean mAppFreezing;
boolean mAttachedHidden; // is our parent window hidden?
boolean mWallpaperVisible; // for wallpaper, what was last vis report?
// Being hidden due to app op request.
return false;
}
+ if (mPermanentlyHidden) {
+ // Permanently hidden until the app exists as apps aren't prepared
+ // to handle their windows being removed from under them.
+ return false;
+ }
if (mPolicyVisibility && mPolicyVisibilityAfterAnim) {
// Already showing.
return false;
}
}
+ public void markPermanentlyHiddenLw() {
+ if (!mPermanentlyHidden) {
+ mPermanentlyHidden = true;
+ hideLw(true, true);
+ }
+ }
+
public void pokeDrawLockLw(long timeout) {
if (isVisibleOrAdding()) {
if (mDrawLock == null) {
pw.println(Integer.toHexString(mSystemUiVisibility));
}
if (!mPolicyVisibility || !mPolicyVisibilityAfterAnim || !mAppOpVisibility
- || mAttachedHidden) {
+ || mAttachedHidden || mPermanentlyHidden) {
pw.print(prefix); pw.print("mPolicyVisibility=");
pw.print(mPolicyVisibility);
pw.print(" mPolicyVisibilityAfterAnim=");
pw.print(" mAppOpVisibility=");
pw.print(mAppOpVisibility);
pw.print(" mAttachedHidden="); pw.println(mAttachedHidden);
+ pw.print(" mPermanentlyHidden="); pw.println(mPermanentlyHidden);
}
if (!mRelayoutCalled || mLayoutNeeded) {
pw.print(prefix); pw.print("mRelayoutCalled="); pw.print(mRelayoutCalled);