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 Type int permission (*只能赋权给system app)
TYPE_SECURE_SYSTEM_OVERLAY 2015 *android.permission.INTERNAL_SYSTEM_WINDOW 覆盖在所有Window上
TYPE_PHONE 2002 *android.permission.INTERNAL_SYSTEM_WINDOW 覆盖在所有application上,但是不覆盖status bar
TYPE_APPLICATION_OVERLAY 2038 android.permission.SYSTEM_ALERT_WINDOW 覆盖所有Activity window,(types between {@link #FIRST_APPLICATION_WINDOW} and {@link #LAST_APPLICATION_WINDOW})

猜你喜欢

转载自blog.csdn.net/qq_36063677/article/details/127005106