Service能够创建界面(addView)吗?

一个Service能够创建界面(addView)吗?

一个app,只有Service,没有Activity,能够通过WindowManager调用addView()添加可视界面吗?

答案是可以,但是能够创建的界面类型(WindowManager.LayoutParams.type)不多,且大多需要android.permission.INTERNAL_SYSTEM_WINDOW权限,这个权限只能授予system app。

在没有Activity的进程中创建显示View,存在两个问题:

  1. 线程,安卓只能在主线程操作UI界面。
  2. token,在启动Activity时实例化ActivityRecord对象创建token(ActivityRecord继承WindowToken类)。

第一个问题好解决,Service生命周期方法onStartCommand()运行在主线程,可以在此方法中定义Handler,service线程通过handler发送消息跨线程通信,在主线程操作UI界面。

    Handler handler;
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 1:
                        CursorLocationView cursorLocationView = new CursorLocationView(BleService.this);
                        params.type = 2015;		//WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
                        mWindowManager.addView(cursorLocationView, params);
                        break;
                }
            }
        };
    }

第二个问题需要解决token问题,WindowManagerService.addWindow()方法中在检查到token==null的情况时通过unprivilegedAppCanCreateTokenWith()检查属性:WindowManager.LayoutParams.type,如果不是特权类型的话返回false。

//WindowManagerService.java
	private boolean unprivilegedAppCanCreateTokenWith(WindowState parentWindow,
            int callingUid, int type, int rootType, IBinder tokenForLog, String packageName) {
        if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {
            ProtoLog.w(WM_ERROR, "Attempted to add application window with unknown token "
                    + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (rootType == TYPE_INPUT_METHOD) {
            ProtoLog.w(WM_ERROR, "Attempted to add input method window with unknown token "
                    + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (rootType == TYPE_VOICE_INTERACTION) {
            ProtoLog.w(WM_ERROR,
                    "Attempted to add voice interaction window with unknown token "
                            + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (rootType == TYPE_WALLPAPER) {
            ProtoLog.w(WM_ERROR, "Attempted to add wallpaper window with unknown token "
                    + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (rootType == TYPE_QS_DIALOG) {
            ProtoLog.w(WM_ERROR, "Attempted to add QS dialog window with unknown token "
                    + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {
            ProtoLog.w(WM_ERROR,
                    "Attempted to add Accessibility overlay window with unknown token "
                            + "%s.  Aborting.", tokenForLog);
            return false;
        }
        if (type == TYPE_TOAST) {
            // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.
            if (doesAddToastWindowRequireToken(packageName, callingUid, parentWindow)) {
                ProtoLog.w(WM_ERROR, "Attempted to add a toast window with unknown token "
                        + "%s.  Aborting.", tokenForLog);
                return false;
            }
        }
        return true;
    }

可以看到以下类型是需要token的,都和用户界面交互强相关:

  • FIRST_APPLICATION_WINDOW~LAST_APPLICATION_WINDOW
  • TYPE_INPUT_METHOD
  • TYPE_VOICE_INTERACTION
  • TYPE_WALLPAPER
  • TYPE_QS_DIALOG
  • TYPE_ACCESSIBILITY_OVERLAY
  • TYPE_TOAST(appInfo.targetSdkVersion < Build.VERSION_CODES.O 时例外)

其余特权类型在token=null的情况下,创建新的Token对象:

//WindowManagerService.java
public int addWindow(Session session, IWindow client, int seq,
            LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,
            Rect outContentInsets, Rect outStableInsets,
            DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,
            InsetsState outInsetsState, InsetsSourceControl[] outActiveControls,
            int requestUserId) {
    		...
            if (token == null) {
                if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
                        rootType, attrs.token, attrs.packageName)) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
                }
                
                // 特权类型!!!!
                if (hasParent) {
                    // Use existing parent window token for child windows.
                    token = parentWindow.mToken;
                } else {
                    final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                    token = new WindowToken(this, binder, type, false, displayContent,
                            session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
                }
            }
}

一些权限类型:

Window Typeintpermission (*只能赋权给system app)
TYPE_SECURE_SYSTEM_OVERLAY2015*android.permission.INTERNAL_SYSTEM_WINDOW覆盖在所有Window上
TYPE_PHONE2002*android.permission.INTERNAL_SYSTEM_WINDOW覆盖在所有application上,但是不覆盖status bar
TYPE_APPLICATION_OVERLAY2038android.permission.SYSTEM_ALERT_WINDOW覆盖所有Activity window,(types between {@link #FIRST_APPLICATION_WINDOW} and {@link #LAST_APPLICATION_WINDOW})
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值