1/** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import installer from '@ohos.bundle.installer'; 17import bundleMonitor from '@ohos.bundle.bundleMonitor'; 18import osAccount from '@ohos.account.osAccount'; 19import hiSysEvent from '@ohos.hiSysEvent'; 20import launcherBundleManager from '@ohos.bundle.launcherBundleManager'; 21import { Log } from '../utils/Log'; 22import { CheckEmptyUtils } from '../utils/CheckEmptyUtils'; 23import { AppItemInfo } from '../bean/AppItemInfo'; 24import { CommonConstants } from '../constants/CommonConstants'; 25import { ResourceManager } from './ResourceManager'; 26import { EventConstants } from '../constants/EventConstants'; 27import { BadgeManager } from '../manager/BadgeManager'; 28import { PreferencesHelper } from './PreferencesHelper'; 29import { BusinessError } from '@ohos.base'; 30import performanceMonitor from '@ohos.arkui.performanceMonitor'; 31 32const TAG = 'LauncherAbilityManager'; 33 34interface BundleStatusCallback { 35 add: (bundleName: string, userId: number) => void; 36 remove: (bundleName: string, userId: number) => void; 37 update: (bundleName: string, userId: number) => void; 38} 39 40/** 41 * Wrapper class for innerBundleManager and formManager interfaces. 42 */ 43class LauncherAbilityManager { 44 private static readonly CURRENT_USER_ID = -2; 45 private readonly mAppMap = new Map<string, AppItemInfo>(); 46 private mUserId: number = 100; 47 private mIsNeedRegisterBundleCallback = false; 48 private readonly mBundleStatusCallback: BundleStatusCallback = { 49 add: (bundleName, userId) => { 50 Log.showDebug(TAG, `PACKAGE_ADDED bundleName: ${bundleName}, userId: ${userId}, mUserId ${this.mUserId}`); 51 this.mUserId === userId && this.notifyLauncherAbilityChange(EventConstants.EVENT_PACKAGE_ADDED, bundleName, userId); 52 }, 53 remove: (bundleName, userId) => { 54 Log.showDebug(TAG, `PACKAGE_REMOVED bundleName: ${bundleName}, userId: ${userId}, mUserId ${this.mUserId}`); 55 this.mUserId === userId && this.notifyLauncherAbilityChange(EventConstants.EVENT_PACKAGE_REMOVED, bundleName, userId); 56 }, 57 update: (bundleName, userId) => { 58 Log.showDebug(TAG, `PACKAGE_CHANGED bundleName: ${bundleName}, userId: ${userId}, mUserId ${this.mUserId}`); 59 this.mUserId === userId && this.notifyLauncherAbilityChange(EventConstants.EVENT_PACKAGE_CHANGED, bundleName, userId); 60 } 61 }; 62 63 private readonly mLauncherAbilityChangeListeners: any[] = []; 64 65 /** 66 * Get desktop application information management object 67 * 68 * @return Desktop application information management object instance 69 */ 70 static getInstance(): LauncherAbilityManager { 71 if (globalThis.LauncherAbilityManagerInstance == null) { 72 globalThis.LauncherAbilityManagerInstance = new LauncherAbilityManager(); 73 } 74 return globalThis.LauncherAbilityManagerInstance; 75 } 76 77 private constructor() { 78 const osAccountManager = osAccount.getAccountManager(); 79 osAccountManager.getOsAccountLocalId((err, localId) => { 80 Log.showDebug(TAG, `getOsAccountLocalId localId ${localId}`); 81 this.mUserId = localId; 82 }); 83 } 84 85 getUserId(): number { 86 return this.mUserId; 87 } 88 89 checkBundleMonitor(): void { 90 if (!this.mIsNeedRegisterBundleCallback) { 91 return; 92 } 93 Log.showInfo(TAG, 'checkBundleMonitor need to register bundle callback'); 94 this.bundleMonitorOn(); 95 } 96 97 bundleMonitorOn(): void { 98 this.mIsNeedRegisterBundleCallback = false; 99 bundleMonitor.on('add', (bundleChangeInfo) => { 100 Log.showInfo(TAG, `add bundleName: ${bundleChangeInfo.bundleName} userId: ${bundleChangeInfo.userId}`); 101 this.mBundleStatusCallback.add(bundleChangeInfo.bundleName, bundleChangeInfo.userId); 102 }); 103 bundleMonitor.on('update', (bundleChangeInfo) => { 104 Log.showInfo(TAG, `update bundleName: ${bundleChangeInfo.bundleName} userId: ${bundleChangeInfo.userId}`); 105 this.mBundleStatusCallback.update(bundleChangeInfo.bundleName, bundleChangeInfo.userId); 106 }); 107 bundleMonitor.on('remove', (bundleChangeInfo) => { 108 Log.showInfo(TAG, `remove bundleName: ${bundleChangeInfo.bundleName} userId: ${bundleChangeInfo.userId}`); 109 this.mBundleStatusCallback.remove(bundleChangeInfo.bundleName, bundleChangeInfo.userId); 110 }); 111 Log.showInfo(TAG, 'bundleMonitorOn register bundle callback finished'); 112 } 113 114 /** 115 * Monitor system application status. 116 * 117 * @params listener: listening object 118 */ 119 async registerLauncherAbilityChangeListener(listener: any):Promise<void> { 120 if (listener != null) { 121 if (this.mLauncherAbilityChangeListeners.length == 0) { 122 try { 123 let isBmsHasInit = await PreferencesHelper.getInstance().get('isBmsHasInit', false); 124 Log.showInfo(TAG, `registerCallback success, isBmsHasInit:${isBmsHasInit}`); 125 if (isBmsHasInit) { 126 this.bundleMonitorOn(); 127 } else { 128 PreferencesHelper.getInstance().put('isBmsHasInit', true); 129 this.mIsNeedRegisterBundleCallback = true; 130 } 131 } catch (errData) { 132 let message = (errData as BusinessError).message; 133 let errCode = (errData as BusinessError).code; 134 Log.showError(TAG, `registerCallback fail errCode:${errCode}, message:${message}`); 135 } 136 } 137 const index = this.mLauncherAbilityChangeListeners.indexOf(listener); 138 if (index == CommonConstants.INVALID_VALUE) { 139 this.mLauncherAbilityChangeListeners.push(listener); 140 } 141 } 142 } 143 144 /** 145 * Cancel monitoring system application status. 146 * 147 * @params listener: listening object 148 */ 149 unregisterLauncherAbilityChangeListener(listener: any): void { 150 if (listener != null) { 151 const index = this.mLauncherAbilityChangeListeners.indexOf(listener); 152 if (index != CommonConstants.INVALID_VALUE) { 153 this.mLauncherAbilityChangeListeners.splice(index, 1); 154 } 155 if (this.mLauncherAbilityChangeListeners.length == 0) { 156 try { 157 bundleMonitor.off('add'); 158 bundleMonitor.off('update'); 159 bundleMonitor.off('remove'); 160 Log.showInfo(TAG, 'unregisterCallback success'); 161 } catch (errData) { 162 let message = (errData as BusinessError).message; 163 let errCode = (errData as BusinessError).code; 164 Log.showError(TAG, `unregisterCallback fail errCode:${errCode}, message:${message}`); 165 } 166 } 167 } 168 } 169 170 private notifyLauncherAbilityChange(event, bundleName: string, userId): void { 171 for (let index = 0; index < this.mLauncherAbilityChangeListeners.length; index++) { 172 this.mLauncherAbilityChangeListeners[index](event, bundleName, userId); 173 } 174 } 175 176 /** 177 * get all app List info from BMS 178 */ 179 async getLauncherAbilityList(): Promise<AppItemInfo[]> { 180 let abilityList = null; 181 await launcherBundleManager.getAllLauncherAbilityInfo(LauncherAbilityManager.CURRENT_USER_ID) 182 .then((res) => { 183 abilityList = res; 184 }) 185 .catch((err) => { 186 Log.showError(TAG, `getLauncherAbilityList error: ${JSON.stringify(err)}`); 187 }); 188 const appItemInfoList = new Array<AppItemInfo>(); 189 if (CheckEmptyUtils.isEmpty(abilityList)) { 190 Log.showDebug(TAG, 'getLauncherAbilityList Empty'); 191 return appItemInfoList; 192 } 193 for (let i = 0; i < abilityList.length; i++) { 194 let appItem = await this.convertToAppItemInfo(abilityList[i]); 195 appItemInfoList.push(appItem); 196 } 197 return appItemInfoList; 198 } 199 200 /** 201 * get AbilityInfos by bundleName from BMS 202 * 203 * @params bundleName Application package name 204 * @return List of entry capabilities information of the target application 205 */ 206 async getLauncherAbilityInfo(bundleName: string): Promise<AppItemInfo[]> { 207 let abilityInfos: launcherBundleManager.LauncherAbilityInfo[]; 208 await launcherBundleManager.getLauncherAbilityInfo(bundleName, this.mUserId) 209 .then((res) => { 210 abilityInfos = res; 211 }) 212 .catch((err) => { 213 Log.showError(TAG, `getLauncherAbilityInfo error: ${JSON.stringify(err)}`); 214 }); 215 const appItemInfoList = new Array<AppItemInfo>(); 216 if (CheckEmptyUtils.isEmpty(abilityInfos) || abilityInfos.length === 0) { 217 Log.showDebug(TAG, 'getLauncherAbilityInfo Empty'); 218 return appItemInfoList; 219 } 220 for (let i = 0; i < abilityInfos.length; i++) { 221 let appItem = await this.convertToAppItemInfo(abilityInfos[i]); 222 appItemInfoList.push(appItem); 223 } 224 return appItemInfoList; 225 } 226 227 /** 228 * get AppItemInfo from BMS with bundleName 229 * @params bundleName 230 * @return AppItemInfo 231 */ 232 async getAppInfoByBundleName(bundleName: string, abilityName?: string): Promise<AppItemInfo | undefined> { 233 let appItemInfo: AppItemInfo | undefined = undefined; 234 // get from cache 235 if (this.mAppMap && this.mAppMap.has(bundleName)) { 236 appItemInfo = this.mAppMap.get(bundleName); 237 } 238 if (appItemInfo && abilityName && appItemInfo.abilityName === abilityName) { 239 return appItemInfo; 240 } 241 // get from system 242 let abilityInfos = new Array<launcherBundleManager.LauncherAbilityInfo>(); 243 await launcherBundleManager.getLauncherAbilityInfo(bundleName, LauncherAbilityManager.CURRENT_USER_ID) 244 .then((res)=>{ 245 if (res && res.length) { 246 abilityInfos = res; 247 } 248 }) 249 .catch((err)=>{ 250 Log.showError(TAG, `getAppInfoByBundleName getLauncherAbilityInfo error: ${JSON.stringify(err)}`); 251 }); 252 if (!abilityInfos || abilityInfos.length === 0) { 253 Log.showDebug(TAG, `${bundleName} has no launcher ability`); 254 return undefined; 255 } 256 let appInfo = abilityInfos[0]; 257 if (abilityName) { 258 appInfo = abilityInfos.find(item => { 259 return item.elementName.abilityName === abilityName; 260 }); 261 } 262 if (!appInfo) { 263 appInfo = abilityInfos[0]; 264 } 265 const data = await this.convertToAppItemInfo(appInfo); 266 return data; 267 } 268 269 private async convertToAppItemInfo(info): Promise<AppItemInfo> { 270 const appItemInfo = new AppItemInfo(); 271 appItemInfo.appName = await ResourceManager.getInstance().getAppNameSync( 272 info.labelId, info.elementName.bundleName, info.elementName.moduleName, info.applicationInfo.label 273 ); 274 appItemInfo.isSystemApp = info.applicationInfo.systemApp; 275 appItemInfo.isUninstallAble = info.applicationInfo.removable; 276 appItemInfo.appIconId = info.iconId; 277 appItemInfo.appLabelId = info.labelId; 278 appItemInfo.bundleName = info.elementName.bundleName; 279 appItemInfo.abilityName = info.elementName.abilityName; 280 appItemInfo.moduleName = info.elementName.moduleName; 281 appItemInfo.keyName = info.elementName.bundleName + info.elementName.abilityName + info.elementName.moduleName; 282 appItemInfo.typeId = CommonConstants.TYPE_APP; 283 appItemInfo.installTime = String(new Date()); 284 appItemInfo.badgeNumber = await BadgeManager.getInstance().getBadgeByBundleSync(info.elementName.bundleName); 285 await ResourceManager.getInstance().updateIconCache(appItemInfo.appIconId, appItemInfo.bundleName, appItemInfo.moduleName); 286 this.mAppMap.set(appItemInfo.bundleName, appItemInfo); 287 return appItemInfo; 288 } 289 290 /** 291 * uninstall application, notice the userId need to be the login user 292 * 293 * @params bundleName application bundleName 294 * @params callback to get result 295 */ 296 async uninstallLauncherAbility(bundleName: string, callback: (resultCode: number) => void): Promise<void> { 297 Log.showInfo(TAG, `uninstallLauncherAbility bundleName: ${bundleName}`); 298 try { 299 const bundlerInstaller = await installer.getBundleInstaller(); 300 bundlerInstaller.uninstall(bundleName, { 301 userId: this.mUserId, 302 installFlag: 0, 303 isKeepData: false 304 }, (err: BusinessError) => { 305 if (err) { 306 callback(CommonConstants.INVALID_VALUE); 307 Log.showError(TAG, `uninstallLauncherAbility failed: ${JSON.stringify(err)}`); 308 } else { 309 callback(CommonConstants.UNINSTALL_SUCCESS); 310 Log.showDebug(TAG, `uninstallLauncherAbility successfully: ${JSON.stringify(err)}`); 311 } 312 }); 313 } catch (err) { 314 let errCode = (err as BusinessError).code; 315 let errMsg = (err as BusinessError).message; 316 Log.showError(TAG, `uninstallLauncherAbility errCode: ${errCode}, errMsg: ${errMsg}`); 317 } 318 } 319 320 /** 321 * start the app 322 * 323 * @params paramAbilityName: Ability name 324 * @params paramBundleName: Application package name 325 */ 326 startLauncherAbility(paramAbilityName: string, paramBundleName: string, paramModuleName: string) { 327 Log.showDebug(TAG, `startApplication abilityName: ${paramAbilityName}, bundleName: ${paramBundleName}, moduleName ${paramModuleName}`); 328 globalThis.desktopContext.startAbility({ 329 bundleName: paramBundleName, 330 abilityName: paramAbilityName, 331 moduleName: paramModuleName 332 }).then(() => { 333 Log.showDebug(TAG, 'startApplication promise success'); 334 performanceMonitor.end('LAUNCHER_APP_LAUNCH_FROM_ICON'); 335 Log.showDebug(TAG, 'performanceMonitor end'); 336 }, (err) => { 337 Log.showError(TAG, `startApplication promise error: ${JSON.stringify(err)}`); 338 }); 339 340 const sysEventInfo = { 341 domain: 'LAUNCHER_APP', 342 name: 'START_ABILITY', 343 eventType: hiSysEvent.EventType.BEHAVIOR, 344 params: { 345 'BUNDLE_NAME': paramBundleName, 346 'ABILITY_NAME': paramAbilityName, 347 'MODULE_NAME': paramModuleName 348 } 349 }; 350 hiSysEvent.write(sysEventInfo, 351 (err, value) => { 352 if (err) { 353 Log.showError(TAG, `startApplication hiSysEvent write error: ${err.code}`); 354 } else { 355 Log.showDebug(TAG, `startApplication hiSysEvent write success: ${value}`); 356 } 357 }); 358 Log.showDebug(TAG, 'performanceMonitor begin'); 359 performanceMonitor.begin('LAUNCHER_APP_LAUNCH_FROM_ICON', performanceMonitor.ActionType.LAST_UP, 360 paramBundleName); 361 } 362 363 /** 364 * start form config ability 365 * 366 * @params paramAbilityName 367 * @params paramBundleName 368 */ 369 startAbilityFormEdit(paramAbilityName: string, paramBundleName: string, paramModuleName: string, paramCardId: number) { 370 Log.showDebug(TAG, `startAbility abilityName: ${paramAbilityName},bundleName: ${paramBundleName}, moduleName: ${paramModuleName} ,paramCardId: ${paramCardId}`); 371 globalThis.desktopContext.startAbility({ 372 bundleName: paramBundleName, 373 abilityName: paramAbilityName, 374 moduleName: paramModuleName, 375 parameters: 376 { 377 formId: paramCardId.toString() 378 } 379 }).then((ret) => { 380 Log.showDebug(TAG, `startAbility ret: ${JSON.stringify(ret)}`); 381 }, (err) => { 382 Log.showError(TAG, `startAbility catch error: ${JSON.stringify(err)}`); 383 }); 384 } 385 386 async getShortcutInfo(paramBundleName: string, callback) { 387 Log.showDebug(TAG, `getShortcutInfo bundleName: ${paramBundleName}`); 388 await launcherBundleManager.getShortcutInfo(paramBundleName) 389 .then(shortcutInfo => { 390 callback(paramBundleName, shortcutInfo); 391 }) 392 .catch(err => { 393 }); 394 } 395 396 /** 397 * start application by uri 398 * 399 * @params paramBundleName application bundle name 400 * @params paramAbilityName application abilit uri 401 */ 402 startLauncherAbilityByUri(paramBundleName: string, abilityUri) { 403 Log.showInfo(TAG, `startLauncherAbilityByUri bundleName:${paramBundleName} abilityUri:${abilityUri}`); 404 const result = globalThis.desktopContext.startAbility({ 405 bundleName: paramBundleName, 406 uri: abilityUri 407 }).then(() => { 408 Log.showDebug(TAG, 'startLauncherAbilityByUri promise success'); 409 }, (err) => { 410 Log.showError(TAG, `startLauncherAbilityByUri promise error: ${JSON.stringify(err)}`); 411 }); 412 Log.showDebug(TAG, `startLauncherAbilityByUri AceApplication : startAbility : ${result}`); 413 } 414 415 cleanAppMapCache() { 416 this.mAppMap.clear(); 417 } 418} 419 420export const launcherAbilityManager = LauncherAbilityManager.getInstance(); 421