11//  Copyright (c) Tailscale Inc & AUTHORS
22//  SPDX-License-Identifier: BSD-3-Clause
33package  com.tailscale.ipn 
4+ 
45import  android.Manifest 
56import  android.app.Application 
67import  android.app.Notification 
@@ -37,6 +38,10 @@ import com.tailscale.ipn.ui.viewModel.AppViewModelFactory
3738import  com.tailscale.ipn.util.FeatureFlags 
3839import  com.tailscale.ipn.util.ShareFileHelper 
3940import  com.tailscale.ipn.util.TSLog 
41+ import  java.io.IOException 
42+ import  java.net.NetworkInterface 
43+ import  java.security.GeneralSecurityException 
44+ import  java.util.Locale 
4045import  kotlinx.coroutines.CoroutineScope 
4146import  kotlinx.coroutines.Dispatchers 
4247import  kotlinx.coroutines.SupervisorJob 
@@ -48,12 +53,10 @@ import kotlinx.coroutines.launch
4853import  kotlinx.serialization.encodeToString 
4954import  kotlinx.serialization.json.Json 
5055import  libtailscale.Libtailscale 
51- import  java.io.IOException 
52- import  java.net.NetworkInterface 
53- import  java.security.GeneralSecurityException 
54- import  java.util.Locale 
56+ 
5557class  App  : UninitializedApp (), libtailscale.AppContext, ViewModelStoreOwner {
5658  val  applicationScope =  CoroutineScope (SupervisorJob () +  Dispatchers .Default )
59+ 
5760  companion  object  {
5861    private  const  val  FILE_CHANNEL_ID  =  " tailscale-files" 
5962    //  Key to store the SAF URI in EncryptedSharedPreferences.
@@ -70,26 +73,34 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
7073      return  appInstance
7174    }
7275  }
76+ 
7377  val  dns =  DnsConfig ()
7478  private  lateinit  var  connectivityManager:  ConnectivityManager 
7579  private  lateinit  var  mdmChangeReceiver:  MDMSettingsChangedReceiver 
7680  private  lateinit  var  app:  libtailscale.Application 
7781  override  val  viewModelStore:  ViewModelStore 
7882    get() =  appViewModelStore
83+ 
7984  private  val  appViewModelStore:  ViewModelStore  by lazy { ViewModelStore () }
8085  var  healthNotifier:  HealthNotifier ?  =  null 
86+ 
8187  override  fun  getPlatformDNSConfig (): String  =  dns.dnsConfigAsString
88+ 
8289  override  fun  getInstallSource (): String  =  AppSourceChecker .getInstallSource(this )
90+ 
8391  override  fun  shouldUseGoogleDNSFallback (): Boolean  =  BuildConfig .USE_GOOGLE_DNS_FALLBACK 
92+ 
8493  override  fun  log (s :  String , s1 :  String ) {
8594    Log .d(s, s1)
8695  }
96+ 
8797  fun  getLibtailscaleApp (): libtailscale.Application  {
8898    if  (! isInitialized) {
8999      initOnce() //  Calls the synchronized initialization logic
90100    }
91101    return  app
92102  }
103+ 
93104  override  fun  onCreate () {
94105    super .onCreate()
95106    appInstance =  this 
@@ -113,6 +124,7 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
113124        getString(R .string.health_channel_description),
114125        NotificationManagerCompat .IMPORTANCE_HIGH )
115126  }
127+ 
116128  override  fun  onTerminate () {
117129    super .onTerminate()
118130    Notifier .stop()
@@ -121,7 +133,9 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
121133    viewModelStore.clear()
122134    unregisterReceiver(mdmChangeReceiver)
123135  }
136+ 
124137  @Volatile private  var  isInitialized =  false 
138+ 
125139  @Synchronized
126140  private  fun  initOnce () {
127141    if  (isInitialized) {
@@ -130,6 +144,7 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
130144    initializeApp()
131145    isInitialized =  true 
132146  }
147+ 
133148  private  fun  initializeApp () {
134149    //  Check if a directory URI has already been stored.
135150    val  storedUri =  getStoredDirectoryUri()
@@ -244,6 +259,7 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
244259        EncryptedSharedPreferences .PrefKeyEncryptionScheme .AES256_SIV ,
245260        EncryptedSharedPreferences .PrefValueEncryptionScheme .AES256_GCM )
246261  }
262+ 
247263  fun  getStoredDirectoryUri (): Uri ?  {
248264    val  uriString =  getEncryptedPrefs().getString(PREF_KEY_SAF_URI , null )
249265    return  uriString?.let  { Uri .parse(it) }
@@ -258,6 +274,7 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
258274    QuickToggleService .updateTile()
259275    TSLog .d(" App" " Set Tile Ready: $ableToStartVPN " 
260276  }
277+ 
261278  override  fun  getModelName (): String  {
262279    val  manu =  Build .MANUFACTURER 
263280    var  model =  Build .MODEL 
@@ -268,10 +285,13 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
268285    }
269286    return  " $manu  $model " 
270287  }
288+ 
271289  override  fun  getOSVersion (): String  =  Build .VERSION .RELEASE 
290+ 
272291  override  fun  isChromeOS (): Boolean  {
273292    return  packageManager.hasSystemFeature(" android.hardware.type.pc" 
274293  }
294+ 
275295  override  fun  getInterfacesAsString (): String  {
276296    val  interfaces:  ArrayList <NetworkInterface > = 
277297        java.util.Collections .list(NetworkInterface .getNetworkInterfaces())
@@ -303,11 +323,13 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
303323    }
304324    return  sb.toString()
305325  }
326+ 
306327  @Throws(
307328      IOException ::class , GeneralSecurityException ::class , MDMSettings .NoSuchKeyException ::class )
308329  override  fun  getSyspolicyBooleanValue (key :  String ): Boolean  {
309330    return  getSyspolicyStringValue(key) ==  " true" 
310331  }
332+ 
311333  @Throws(
312334      IOException ::class , GeneralSecurityException ::class , MDMSettings .NoSuchKeyException ::class )
313335  override  fun  getSyspolicyStringValue (key :  String ): String  {
@@ -317,6 +339,7 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
317339    }
318340    return  setting.value?.toString() ? :  " " 
319341  }
342+ 
320343  @Throws(
321344      IOException ::class , GeneralSecurityException ::class , MDMSettings .NoSuchKeyException ::class )
322345  override  fun  getSyspolicyStringArrayJSONValue (key :  String ): String  {
@@ -332,6 +355,7 @@ class App : UninitializedApp(), libtailscale.AppContext, ViewModelStoreOwner {
332355      throw  MDMSettings .NoSuchKeyException ()
333356    }
334357  }
358+ 
335359  fun  notifyPolicyChanged () {
336360    app.notifyPolicyChanged()
337361  }
@@ -374,19 +398,23 @@ open class UninitializedApp : Application() {
374398      }
375399    }
376400  }
401+ 
377402  protected  fun  setUnprotectedInstance (instance :  UninitializedApp ) {
378403    appInstance =  instance
379404  }
405+ 
380406  protected  fun  setAbleToStartVPN (rdy :  Boolean ) {
381407    getUnencryptedPrefs().edit().putBoolean(ABLE_TO_START_VPN_KEY , rdy).apply ()
382408  }
383409  /* * This function can be called without initializing the App. */ 
384410  fun  isAbleToStartVPN (): Boolean  {
385411    return  getUnencryptedPrefs().getBoolean(ABLE_TO_START_VPN_KEY , false )
386412  }
413+ 
387414  private  fun  getUnencryptedPrefs (): SharedPreferences  {
388415    return  getSharedPreferences(UNENCRYPTED_PREFERENCES , MODE_PRIVATE )
389416  }
417+ 
390418  fun  startVPN () {
391419    val  intent =  Intent (this , IPNService ::class .java).apply  { action =  IPNService .ACTION_START_VPN  }
392420    //  FLAG_UPDATE_CURRENT ensures that if the intent is already pending, the existing intent will
@@ -411,6 +439,7 @@ open class UninitializedApp : Application() {
411439      TSLog .e(TAG , " startVPN hit exception: $e " 
412440    }
413441  }
442+ 
414443  fun  stopVPN () {
415444    val  intent =  Intent (this , IPNService ::class .java).apply  { action =  IPNService .ACTION_STOP_VPN  }
416445    try  {
@@ -421,6 +450,7 @@ open class UninitializedApp : Application() {
421450      TSLog .e(TAG , " stopVPN hit exception in startService(): $e " 
422451    }
423452  }
453+ 
424454  fun  restartVPN () {
425455    val  intent = 
426456        Intent (this , IPNService ::class .java).apply  { action =  IPNService .ACTION_RESTART_VPN  }
@@ -432,19 +462,22 @@ open class UninitializedApp : Application() {
432462      TSLog .e(TAG , " restartVPN hit exception in startService(): $e " 
433463    }
434464  }
465+ 
435466  fun  createNotificationChannel (id :  String , name :  String , description :  String , importance :  Int ) {
436467    val  channel =  NotificationChannel (id, name, importance)
437468    channel.description =  description
438469    notificationManager =  NotificationManagerCompat .from(this )
439470    notificationManager.createNotificationChannel(channel)
440471  }
472+ 
441473  fun  notifyStatus (
442474      vpnRunning :  Boolean ,
443475      hideDisconnectAction :  Boolean ,
444476      exitNodeName :  String?  = null
445477  ) {
446478    notifyStatus(buildStatusNotification(vpnRunning, hideDisconnectAction, exitNodeName))
447479  }
480+ 
448481  fun  notifyStatus (notification :  Notification ) {
449482    if  (ActivityCompat .checkSelfPermission(this , Manifest .permission.POST_NOTIFICATIONS ) != 
450483        PackageManager .PERMISSION_GRANTED ) {
@@ -459,6 +492,7 @@ open class UninitializedApp : Application() {
459492    }
460493    notificationManager.notify(STATUS_NOTIFICATION_ID , notification)
461494  }
495+ 
462496  fun  buildStatusNotification (
463497      vpnRunning :  Boolean ,
464498      hideDisconnectAction :  Boolean ,
@@ -504,6 +538,7 @@ open class UninitializedApp : Application() {
504538    }
505539    return  builder.build()
506540  }
541+ 
507542  fun  updateUserDisallowedPackageNames (packageNames :  List <String >) {
508543    if  (packageNames.any { it.isEmpty() }) {
509544      TSLog .e(TAG , " updateUserDisallowedPackageNames called with empty packageName(s)" 
@@ -512,6 +547,7 @@ open class UninitializedApp : Application() {
512547    getUnencryptedPrefs().edit().putStringSet(DISALLOWED_APPS_KEY , packageNames.toSet()).apply ()
513548    this .restartVPN()
514549  }
550+ 
515551  fun  disallowedPackageNames (): List <String > {
516552    val  mdmDisallowed = 
517553        MDMSettings .excludedPackages.flow.value.value?.split(" ," ? :  emptyList()
@@ -553,4 +589,4 @@ open class UninitializedApp : Application() {
553589          //  Android Connectivity Service https://github.com/tailscale/tailscale/issues/14128
554590          " com.google.android.apps.scone" 
555591      )
556- }
592+ }
0 commit comments