Android Wear app not installing through handset

android, apk, wear-os

Solution

To Answer @odiggity specifically, in build.gradle file of your phone app, you should mention the exact name of the wear app folder. If you created the project in Android Studio, then your build.gradle should look like this:

                      wearApp project(':wear')

This can happen because of the following reasons:

- Wear & Mobile app's `"permissions"` are not same (Wear App permissions should be a subset of Mobile App permissions).

This is not documented anywhere on developers website, however I found this with my personal experience. The reason I can think of behind this restriction is, Google want to prevent sneaky developers to exploit users & their privacy.

- `Package name` of Wear & Mobile apps are not matching.

- `VersionNumber` & `VersionName` of both Wear & Mobile app not matching

- `Application ID` (build.gradle file) of Mobile & wear app are not matching.

- by default wearable packages are only included in `release` builds

- both apps must be `signed` with the same key

- as of Android Studio 3.0 apk builds created with run are `test only builds` that don't allow installation on wear (returnCode -15, see this information)

- Asset Compression

If you are using Eclipse for development, make sure you turn off the "Asset Compression" otherwise the ".apk" file in raw folder will get `double compressed` & the `phone won't be able to recognize` if the Mobile app is packaging wear app or not.

Best Solution:

Use Android Studio.

- Create Android Project

- Select Phone & Wear project

- Follow the steps for project creation

- Copy all the permissions used in Wear app to Mobile app's Manifest or vice versa

Debugging

When you don't find the wear app on the wearable you can always look into logs of both devices to see what is going on. You can filter on `WearablePkgInstaller` to find all logging related to wearable package installation.

From the Wear OS app on you device trigger the "resync apps" option from "advanced settings" and check the logs. Alternative way to sync only the wearable for your app is to reinstall your app. At that point the wearable is also synced for your package.

Device logging should list something like:

11-07 14:58:53.127 3330-8739/? I/WearablePkgInstaller: Setting DataItem to install wearable apps for com.spotify.music

With com.spotify.music being your app Id. This is just an example for Spotify.

And on the watch (debug over bluetooth or USB) you can find logging with this same filter showing issues or success:

11-07 15:00:02.533 1032-1048/? I/WearablePkgInstaller: Package com.spotify.music was installed.

Most error messages are self explanatory. You can find many examples of these errors in the source code of WearPackageInstallerService class. Some however are just a returnCode. For these return codes check the values in this PackageManager source code.

Problem

I'm trying to get a wearable app installed through an Android handset using the 'Package with Android Studio method' found here but it's not working. The apk is never installed to the wearable device. This is the logcat output: ``` 07-28 15:11:54.107 766-820/? W/PackageManager﹕ Unknown permission com.google.android.wearable.READ_SETTINGS in package com.google.android.gms 07-28 15:11:54.117 766-820/? W/PackageManager﹕ Not granting permission com.google.android.gm.permission.AUTO_SEND to package com.google.android.wearable.app (protectionLevel=2 flags=0x88be44) 07-28 15:11:54.117 766-820/? W/PackageManager﹕ Not granting permission android.permission.MEDIA_CONTENT_CONTROL to package com.google.android.wearable.app (protectionLevel=18 flags=0x88be44) 07-28 15:11:55.047 632-632/? D/WearablePkgInstaller﹕ Got PackageUpdateReceiver message Intent { act=android.intent.action.PACKAGE_REMOVED dat=package:my.package.name flg=0x4000010 cmp=com.google.android.wearable.app/com.google.android.clockwork.companion.packagemanager.PackageUpdateReceiver (has extras) } 07-28 15:11:55.177 632-632/? D/WearablePkgInstaller﹕ Got PackageUpdateReceiver message Intent { act=android.intent.action.PACKAGE_ADDED dat=package:my.package.name flg=0x4000010 cmp=com.google.android.wearable.app/com.google.android.clockwork.companion.packagemanager.PackageUpdateReceiver (has extras) } ``` As a side note I am able to package manually (also described in the link above) and the apk gets installed on the wearable device when I run it on the handset. I am using buildToolsVersion 20.0.0, I'm running Android Studio 0.8.2 and have this line in my handset module's build.gradle: ``` wearApp project(':wearable') ``` I've run out of ideas on how to debug this, the logs seem pretty useless. Any ideas? EDIT: Going to post relevant sections of Manifest and build.gradle for both the handset and wearable module. Handset Manifest ``` <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="my.package.name" android:installLocation="auto" android:versionCode="259" android:versionName="4.6.1"> <!-- =========== --> <!-- PERMISSIONS --> <!-- =========== --> <permission android:name="my.app.name.permission.C2D_MESSAGE" android:protectionLevel="signature"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <!-- ====================== --> <!-- APPLICATION PROPERTIES --> <!-- ====================== --> <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19" /> <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:xlargeScreens="true"/> <application android:name=".AppState" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.MyApp" android:hardwareAccelerated="true"> <!-- ==================== --> <!-- GOOGLE PLAY SERVICES --> <!-- ==================== --> <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/> <!-- ================== --> <!-- Android Wear --> <!-- ================== --> <service android:name=".wear.DataLayerListenerService" > <intent-filter> <action android:name="com.google.android.gms.wearable.BIND_LISTENER" /> </intent-filter> </service> <!-- This is used for manual packaging I have this commented out when --> <!-- packaging with Android Studio--> <!--<meta-data android:name="com.google.android.wearable.beta.app"--> <!--android:resource="@xml/wearable_app_desc"/>--> ...The rest of the activities </application> </manifest> ``` Handset build.gradle ``` buildscript { repositories { maven { url 'http://download.crashlytics.com/maven' } } dependencies { } } apply plugin: 'com.android.application' apply plugin: 'crashlytics' apply plugin: 'newrelic' repositories { mavenCentral() maven { url 'http://download.crashlytics.com/maven' } } dependencies { compile fileTree(dir: 'libs', include: '*.jar') compile 'com.android.support:support-v4:20.0.+' compile 'com.android.support:appcompat-v7:20.0.+' compile project(':facebook') compile project(':mopub-sdk') compile project(':GooglePlay') compile 'com.newrelic.agent.android:android-agent:3.+' compile 'com.crashlytics.android:crashlytics:1.+' androidTestCompile fileTree(dir: 'tests/libs', include: '*.jar') wearApp project(':wearable') } android { compileSdkVersion 19 buildToolsVersion '20.0.0' //Build type is debug to avoid conflict with Proguard testBuildType = "debug" defaultConfig { testApplicationId "my.package.name.test" testInstrumentationRunner "com.zutubi.android.junitreport.JUnitReportTestRunner" } lintOptions { // We do not want to abort the build due to lint errors abortOnError false } sourceSets { // Main is the default unless stated otherwise main { manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] resources.srcDirs = ['src'] aidl.srcDirs = ['src'] renderscript.srcDirs = ['src'] res.srcDirs = ['res'] assets.srcDirs = ['assets'] } // Testing androidTest.setRoot('tests') androidTest { java.srcDirs = ['tests/src'] res.srcDirs = ['tests/res'] } // Cannot add beta icons in here because custom flavour source sets are created // during compilation and name duplication will result in a crash } signingConfigs { debug { storeFile file("**") storePassword "***" keyAlias "***" keyPassword "***" } release { storeFile file("***") storePassword "***" keyAlias "***" keyPassword "***" } } buildTypes { // Development configuration debug { debuggable true jniDebugBuild true signingConfig signingConfigs.debug runProguard false } // Release configuration release { debuggable false jniDebugBuild false signingConfig signingConfigs.release // COMMENTED PROGUARD OUT FOR NOW TO SEE IF IT WILL HELP // // Configure ProGuard // runProguard true // // General configuration // proguardFile 'proguard/proguard.cfg' // // Add all of our component-specific configurations (excluding the Android generic, as we want it to be last) // FileTree tree = fileTree(dir: 'proguard', include: '*.txt', exclude: 'Android.txt') // tree.each {File file -> // proguardFile file.getCanonicalPath() // } // // Add a fallback configuration for all Android apps // proguardFile 'proguard/Android.txt' } // Release configuration, but debuggable and without ProGuard // Used for testing features like G+ and in-app billing where a release config is required staging { debuggable true jniDebugBuild true signingConfig signingConfigs.release runProguard false } } productFlavors { production { applicationId "my.package.name" } internalBeta { applicationId "my.internalbetapackage.name" // Beta icons sourceSets.internalBeta.res.srcDirs = ['res-beta/internal'] } externalBeta { applicationId "my.externalbetapackage.name" // Beta icons sourceSets.externalBeta.res.srcDirs = ['res-beta/external'] } testing{ applicationId "my.package.name" } } // Without this, gradle will complain that duplicate files were added to the APK, see: // http://stackoverflow.com/questions/20673888/duplicate-files-copied-android-studio-0-4-0 packagingOptions { exclude 'META-INF/LICENSE.txt' // twitter4j exclude 'META-INF/ASL2.0' // jackson exclude 'META-INF/LICENSE' // jackson exclude 'META-INF/NOTICE' // jackson } } task makeTestApks { dependsOn "assembleProductionRelease" dependsOn "assembleProductionTest" } ``` Wearable Manifest ``` <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="my.package.name"> <uses-feature android:name="android.hardware.type.watch" android:required="false"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@android:style/Theme.DeviceDefault" > <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" /> <activity android:name=".WearReaderActivity" android:label="MyApp" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="my.package.name.READ"/> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name=".wear.DataLayerListenerService" > <intent-filter> <action android:name="com.google.android.gms.wearable.BIND_LISTENER" /> </intent-filter> </service> </application> </manifest> ``` Wearable build.gradle ``` repositories { mavenCentral() } apply plugin: 'com.android.application' apply plugin: 'newrelic' android { compileSdkVersion 19 buildToolsVersion '20.0.0' defaultConfig { minSdkVersion 19 targetSdkVersion 19 versionCode 259 versionName "4.6.1" } signingConfigs { debug { keyAlias '***' keyPassword '***' storeFile file('sameAsHandset/debug.keystore') storePassword '***' } release { storeFile file("sameAsHandset/android.keystore") storePassword "***" keyAlias "***" keyPassword "***" } } buildTypes { release { debuggable true jniDebugBuild false signingConfig signingConfigs.release } } productFlavors { production { applicationId "my.package.name" } internalBeta { applicationId "my.internalBetaPackage.name" // Beta icons sourceSets.internalBeta.res.srcDirs = ['res-beta/internal'] } externalBeta { applicationId "my.externalBetaPackage.name" // Beta icons sourceSets.externalBeta.res.srcDirs = ['res-beta/external'] } testing{ applicationId "my.package.name" } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':GooglePlay') compile 'com.newrelic.agent.android:android-agent:3.+' } ```

Original source

Related problems