]> glassweightruler.freedombox.rocks Git - waydroid.git/blob - tools/services/user_manager.py
logging: Use RotatingFileHandler to trim logfile at 5MB
[waydroid.git] / tools / services / user_manager.py
1 # Copyright 2021 Erfan Abdi
2 # SPDX-License-Identifier: GPL-3.0-or-later
3 import glob
4 import logging
5 import os
6 import threading
7 import tools.config
8 import tools.helpers.net
9 from tools.interfaces import IUserMonitor
10 from tools.interfaces import IPlatform
11 from gi.repository import GLib
12
13 stopping = False
14
15 def start(args, session, unlocked_cb=None):
16 waydroid_data = session["waydroid_data"]
17 apps_dir = session["xdg_data_home"] + "/applications/"
18
19 system_apps = [
20 "com.android.calculator2",
21 "com.android.camera2",
22 "com.android.contacts",
23 "com.android.deskclock",
24 "com.android.documentsui",
25 "com.android.email",
26 "com.android.gallery3d",
27 "com.android.inputmethod.latin",
28 "com.android.settings",
29 "com.google.android.gms",
30 "org.lineageos.aperture",
31 "org.lineageos.eleven",
32 "org.lineageos.etar",
33 "org.lineageos.jelly",
34 "org.lineageos.recorder"
35 ]
36
37
38 def prepend_list(old_list, new_list):
39 for s in reversed(new_list):
40 if s not in old_list:
41 old_list.insert(0, s)
42
43 def glib_key_file_get_string_list(key_file, group, key):
44 try:
45 return key_file.get_string_list(group, key)
46 except:
47 return []
48
49 def glib_key_file_prepend_string_list(key_file, group, key, new_list):
50 old_list = glib_key_file_get_string_list(key_file, group, key)
51 prepend_list(old_list, new_list)
52 key_file.set_string_list(group, key, new_list)
53
54 def glib_key_file_has_value(key_file, group, key):
55 try:
56 key_file.get_value(group, key)
57 return True
58 except:
59 return False
60
61
62 # Creates, deletes, or updates desktop file
63 def updateDesktopFile(appInfo):
64 if appInfo is None:
65 return
66
67 showApp = False
68 for cat in appInfo["categories"]:
69 if cat.strip() == "android.intent.category.LAUNCHER":
70 showApp = True
71 if not showApp:
72 try:
73 os.remove(desktop_file_path)
74 except:
75 pass
76
77 packageName = appInfo["packageName"]
78
79 desktop_file_path = apps_dir + "/waydroid." + packageName + ".desktop"
80 desktop_file = GLib.KeyFile()
81 try:
82 flags = GLib.KeyFileFlags.KEEP_COMMENTS | GLib.KeyFileFlags.KEEP_TRANSLATIONS
83 desktop_file.load_from_file(desktop_file_path, flags)
84 except:
85 pass
86
87 desktop_file.set_string("Desktop Entry", "Type", "Application")
88 desktop_file.set_string("Desktop Entry", "Name", appInfo["name"])
89 desktop_file.set_string("Desktop Entry", "Exec", f"waydroid app launch {packageName}")
90 desktop_file.set_string("Desktop Entry", "Icon", f"{waydroid_data}/icons/{packageName}.png")
91 glib_key_file_prepend_string_list(desktop_file, "Desktop Entry", "Categories", ["X-WayDroid-App"])
92 desktop_file.set_string_list("Desktop Entry", "X-Purism-FormFactor", ["Workstation", "Mobile"])
93 glib_key_file_prepend_string_list(desktop_file, "Desktop Entry", "Actions", ["app_settings"])
94 if packageName in system_apps and not glib_key_file_has_value(desktop_file, "Desktop Entry", "NoDisplay"):
95 desktop_file.set_boolean("Desktop Entry", "NoDisplay", True)
96
97 desktop_file.set_string("Desktop Action app_settings", "Name", "App Settings")
98 desktop_file.set_string("Desktop Action app_settings", "Exec", f"waydroid app intent android.settings.APPLICATION_DETAILS_SETTINGS package:{packageName}")
99 desktop_file.set_string("Desktop Action app_settings", "Icon", f"{waydroid_data}/icons/com.android.settings.png")
100
101 desktop_file.save_to_file(desktop_file_path)
102
103
104 def updateWaydroidDesktopFile(hide):
105 desktop_file_path = apps_dir + "/Waydroid.desktop"
106 # If the user has set the desktop file as read-only, we won't replace it
107 if os.path.isfile(desktop_file_path) and not os.access(desktop_file_path, os.W_OK):
108 logging.info(f"Desktop file '{desktop_file_path}' is not writeable, not updating it")
109 return
110
111 desktop_file = GLib.KeyFile()
112 try:
113 flags = GLib.KeyFileFlags.KEEP_COMMENTS | GLib.KeyFileFlags.KEEP_TRANSLATIONS
114 desktop_file.load_from_file(desktop_file_path, flags)
115 except:
116 pass
117
118 desktop_file.set_string("Desktop Entry", "Type", "Application")
119 desktop_file.set_string("Desktop Entry", "Name", "Waydroid")
120 desktop_file.set_string("Desktop Entry", "Exec", "waydroid show-full-ui")
121 glib_key_file_prepend_string_list(desktop_file, "Desktop Entry", "Categories", ["X-WayDroid-App", "Utility"])
122 desktop_file.set_string_list("Desktop Entry", "X-Purism-FormFactor", ["Workstation", "Mobile"])
123 desktop_file.set_string("Desktop Entry", "Icon", "waydroid")
124 desktop_file.set_boolean("Desktop Entry", "NoDisplay", hide)
125
126 desktop_file.save_to_file(desktop_file_path)
127
128 def userUnlocked(uid):
129 cfg = tools.config.load(args)
130 logging.info("Android with user {} is ready".format(uid))
131
132 if cfg["waydroid"]["auto_adb"] == "True":
133 try:
134 tools.helpers.net.adb_connect(args)
135 except:
136 pass
137
138 platformService = IPlatform.get_service(args)
139 if platformService:
140 if not os.path.exists(apps_dir):
141 os.mkdir(apps_dir, 0o700)
142 appsList = platformService.getAppsInfo()
143 for app in appsList:
144 updateDesktopFile(app)
145 for existing in glob.iglob(f'{apps_dir}/waydroid.*.desktop'):
146 if os.path.basename(existing) not in map(lambda appInfo: f"waydroid.{appInfo['packageName']}.desktop", appsList):
147 os.remove(existing)
148 multiwin = platformService.getprop("persist.waydroid.multi_windows", "false")
149 updateWaydroidDesktopFile(multiwin == "true")
150 if unlocked_cb:
151 unlocked_cb()
152
153 def packageStateChanged(mode, packageName, uid):
154 platformService = IPlatform.get_service(args)
155 if platformService:
156 desktop_file_path = apps_dir + "/waydroid." + packageName + ".desktop"
157 if mode == IUserMonitor.PACKAGE_REMOVED:
158 try:
159 os.remove(desktop_file_path)
160 except:
161 pass
162 else:
163 appInfo = platformService.getAppInfo(packageName)
164 updateDesktopFile(appInfo)
165
166 def service_thread():
167 while not stopping:
168 IUserMonitor.add_service(args, userUnlocked, packageStateChanged)
169
170 global stopping
171 stopping = False
172 args.user_manager = threading.Thread(target=service_thread)
173 args.user_manager.start()
174
175 def stop(args):
176 global stopping
177 stopping = True
178 try:
179 if args.userMonitorLoop:
180 args.userMonitorLoop.quit()
181 except AttributeError:
182 logging.debug("UserMonitor service is not even started")