20

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.+'
}
odiggity
  • 4,117
  • 7
  • 34
  • 41
  • Have you tried the DataLayer sample that comes from the SDK Manager? It shows how to build and package an application for both phone and wear, you should check this to make sure it works. Then you can compare the XML files to see what might be different with your application. – Wayne Piekarski Jul 28 '14 at 22:07
  • @WaynePiekarski yes I have and it works fine. I've tried comparing them to mine and everything looks fine, however my app is more complex than it with different flavours so there are quite a few differences – odiggity Jul 29 '14 at 15:10
  • I am not sure that you can compile with api 19 instead of api 20. Also wear and mobile app should have the same permissions. – Gabriele Mariotti Jul 29 '14 at 17:32
  • @GabrieleMariotti the sample DataLayer compiles with 19. Tried it with 20 anyways and it still isn't working. I believe your second suggestions is incorrect, the wearable cannot have a permission that the handset doesn't have but it is fine that the handset has permissions the wearable doesn't. My wearable doesn't have any permissions. – odiggity Jul 29 '14 at 18:00
  • @odiggity you are right about the second point! My mistake. – Gabriele Mariotti Jul 29 '14 at 18:11
  • I notice you declare the version number in AndroidManifest.xml for the handset, but in the build.gradle for the wearable. Can you do both of them in the build.gradle file, just like the DataLayer sample does? It is important that these are identical, and just want to be sure on this. – Wayne Piekarski Jul 29 '14 at 19:33
  • @WaynePiekarski I'm pretty sure the version number for the wearable doesn't have to be the same as the version number for the handset. I tried making sure they were the same and moving the version to the build.gradle on the handset app anyways and it still didn't work. – odiggity Jul 29 '14 at 22:12
  • @jaumard I sort of got it working. The flavours were messing it up. I had originally created the wear app with a beta flavour and then tried building it with a production flavour. I had to create a new project with the production flavour and copy over all the code. Also I have to use Build -> Generate signed apk and select the Key store path, specifying the paths to the same key store in the build.gradle file wasn't working for some reason. Hope that helps :) – odiggity Nov 28 '14 at 18:10
  • @odiggity I believe I followed everything that is suggested here in the post, but yet the mobile apk embedded with the wear apk gets installed on my mobile device, whereas a notification (scanned security threat) would appear on my wear virtual emulator at most... Could you help me out here? Here's my post in more detail: http://stackoverflow.com/questions/36266770/wearable-app-does-not-install-from-mobile-apk – DaveNOTDavid Mar 29 '16 at 15:31
  • For error -103, it's because gradle is not signing the wear app. I was running into this and it turns out I had to duplicate a signing config for the wear app in my build.gradle as it was not being signed at all. – Learn OpenGL ES Nov 22 '16 at 15:26

8 Answers8

25

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.

  1. Create Android Project
  2. Select Phone & Wear project
  3. Follow the steps for project creation
  4. 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.

hcpl
  • 17,382
  • 7
  • 72
  • 73
Umer Farooq
  • 7,356
  • 7
  • 42
  • 67
  • 4
    +1 buddy - for me it was the permissions, they had to be exactly the same. – Ken Wolf Oct 16 '14 at 12:40
  • 1
    Thanks. One hour spent figuring out what was happening and it was caused because my mobile/AndroidManifest.xml was missing wear app permissions. – HyLian Feb 01 '15 at 19:53
  • I found that the mobile manifest could be a superset of the wear permissions. However, the wear manifest did not need to have all the mobile permissions. I imagine this is because there is no way otherwise for a user to know what wear permissions they are accepting if it does not show during the mobile install – Sanketh Katta Feb 02 '15 at 20:42
  • @KenWolf not completely the same but the permissions in the wear app has to be defined in the mobile app also. So wear app permissions must be a subset of mobile apps permissions. Mobile app itself can have more permissions. Also if that is the issue logging will clearly state what permission prevented installation. – hcpl Nov 08 '18 at 08:09
5

I had ussue with wear product flavors. I had flavors in both handheld and wear apps. To solve my issue I used next code in build.gradle handheld app file:

freeWearApp project(path: ':wear', configuration: 'wearfreeRelease')
fullWearApp project(path: ':wear', configuration: 'wearfullRelease')

where, free and full are flavors from handheld module, wearfree and wearfull are flavors from wear mudule, Release is name of wear module buildtype.

And don't forget publishNonDefault true in the android block of the wear gradle.

PavelGP
  • 1,681
  • 1
  • 13
  • 11
  • This did the trick for me, thanks! More documentation regarding this can be found here: http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Library-Publication – Joen93 Jul 19 '16 at 09:41
  • @PavelGP Can you please elaborate on the solution that worked for you – Dibzmania Jan 21 '17 at 00:46
  • Hm, it was a year ago) But I described in the post above my case. I have some flavors in both handheld module and wear module, to configure that the flavor from the handheld app must work with the proper flavor from the wear app module I have used solution described above. And that't it. This code is located in the dependencies section of build.gradle of the handheld app, as far as I remember. – PavelGP Jan 30 '17 at 14:41
4

I had this problem even after checking the package names, application IDs, and making sure the wear module didn't have any permissions that the handheld module didn't.

My problem was that I was requesting the permission android.permission.BIND_NOTIFICATION_LISTENER_SERVICE in AndroidManifest.xml for the wearable module. It seems that this should only be requested for the handheld module.

Denley Bihari
  • 591
  • 3
  • 8
2

as far as I know product flavors are not supported at the moment, either use the manual build (asset or raw) or remove the product flavors.

Demoritas
  • 21
  • 1
2

From my experience it only works when building a signed release build. When deploying a debug build it never installed the wear app for me. With signed release it worked with buildToolsVersion "19.1.0".

In this specific case I can see that you are using "compile project(':GooglePlay')". Is that the latest version? (compile 'com.google.android.gms:play-services-wearable:+')

Heinrisch
  • 5,835
  • 4
  • 33
  • 43
  • 7
    I found this in the [docs](http://goo.gl/FrIt0Q): *Note*: The automatic installation of wearable apps does not work when you are signing apps with a debug key and only works with release keys. See Packaging Wearable Apps for complete information on how to properly package wearable apps. – zim Sep 05 '14 at 20:38
2

Just wanted to add something that I haven't seen in many answers.

You have to match the applicationId in your handheld app and wear app.

I previously thought that you needed to have <uses-feature android:name="android.hardware.type.watch" /> in both the handheld and watch manifest, with add: android:required="true" for the wearable manifest and android:required="false" for the device.

THIS IS FALSE. You do not need the above in the handheld manifest. In fact with Wear 2.0 coming up Google has made some changes that will not allow you to upload any apk with minSdk lower than 23 if <uses-feature android:name="android.hardware.type.watch" /> is in your handheld app. Sorry for any confusion.

CJ Barnwell
  • 43
  • 1
  • 7
  • So you mean I need the `` on the *handheld*?! – Jonas Borggren Apr 14 '16 at 16:47
  • Yes! It needs to be in the AndroidManifest.xml file for the device app! – CJ Barnwell Apr 25 '16 at 07:30
  • 4
    @JonasB No, he is wrong. You don't need `` in the handheld manifest, only on the wearable. – Thomas Vos Jun 02 '16 at 09:56
  • @SuperThomasLab I'm not sure why you think this. If you have any features in your handheld app that send things to the watch, you DO need I'm updating the post with more information, but everytime I've tried... when I do a release build of the project, it won't bundle the wear app unless I include the uses-feature command. – CJ Barnwell Jun 04 '16 at 17:00
  • 1
    @SuperThomasLab was correct. I made the changes in the answer. – CJ Barnwell Nov 27 '16 at 05:17
1

I've been having this issue recently, and in the end the following fixed it;

  • Uninstall previous app
  • Restart Phone
  • Restart wear
  • Disconnect and reconnect wear
  • Re-Sync apps in Android Wear application

And then it popped up on the watch. Before this I tried everything and was convinced the build was not working right

The Applicationist
  • 485
  • 1
  • 4
  • 12
  • I don't see how that could help. If you want to trigger installation from wear app you can reinstall the app or manually trigger resync of all apps in the wear os app. No need the restart, on a linux based system restart has rarely any benefits. If it's still not happening after forcing sync check logs to see what prevents the installation. – hcpl Nov 08 '18 at 08:11
0

Got the same problem, it turned out that newrelic is instrumenting the Android Wear App. Try to comment the line: // apply plugin: 'newrelic'

I couldn't find a way to tell newrelic to keep away from the the wear app...

Jose Parente
  • 126
  • 1
  • 4