2

I have a home made logging system in my app. Of the things that it does is preform alot of logging (to file and logcat) while in development and can be turned off completely with one varible change. For a functionality example:

static public  final boolean DEVELOPMENT_VERBOSE = true;

public static void developmentLogMessage(String message) {
    if (DEVELOPMENT_VERBOSE)
        Log.i("com.xxx.app",  message);
}

The problem (maybe more an annoyance) I have is that I must remember to set DEVELOPMENT_VERBOSE = false for release. Is there a way in code to detect when the app is finalized for release (say checking for a signed apk for example) so I can set DEVELOPMENT_VERBOSE to false programmatically?

I looked at Detect if app was downloaded from Android Market but it seems my app has a signature even before signing it for the market.

try {
    PackageManager manager = context.getPackageManager(); 
    PackageInfo appInfo = manager.getPackageInfo(
                    "com.xxx.app", PackageManager.GET_SIGNATURES
        );

    System.out.println(appInfo.signatures[0].toCharsString());
} catch (NameNotFoundException e) {
}

I was hoping the signatures array would be empty and I could key of of that. But no go.

Community
  • 1
  • 1
Sandstone
  • 148
  • 2
  • 8

3 Answers3

4

You can use ProGuard to completely turn appropriate logs off when building a release. ProGuard can do a lot of interesting stuff. Among other things it can shrink unneeded code during the building process. For example, if you use debug log (Log.d()) during development, but want to disable it in release then you can add these lines to your proguard.cfg:

-assumenosideeffects class android.util.Log {
  public static int d(...);
}

To enable ProGuard, set the property

proguard.config=proguard.cfg

to your project.properties (if you use default locations). Be noted that ProGuard will also do some other things by default so you probably should take some additional steps when releasing your project. At least you certainly want to save generated mapping.txt file. See the ProGuard guide for more details.

praetorian droid
  • 2,989
  • 1
  • 17
  • 19
  • This work for simple log exclusion but I'm not sure about removing large sequences of code. For example code to generate a large/complex string to log. – Sandstone Nov 30 '11 at 01:28
  • This technique is for disabling effect of calling any method in any class unless such a method returns a value, that is used somewhere. In the example above it's an `android.util.Log.d()` method. With this approach ProGuard will remove any calls to this method as if it was never called. So what you had in mind by "code to generate a large/complex string to log"? – praetorian droid Nov 30 '11 at 06:51
0

You can look at which certificate was used to sign the app, and act accordingly.

For example:

  for (Signature sig : getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures) {
    // Get some (pseudo)uniqe value:
    long sigHash = Arrays.hashCode(sig.toByteArray());
    if (sigHash == releaseSigHash) DEVELOPMENT_VERBOSE = false;
  }

Here is what I do for Google MapView, to decide which API Key to use, which is a similar problem

  for (Signature sig : getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES).signatures) {
    MessageDigest m = MessageDigest.getInstance("MD5");
    m.update(sig.toByteArray());
    md5 = new BigInteger(1, m.digest()).toString(16);

    Log.d("findApiNøgle", "md5fingerprint: "+md5);

    // Jacobs debug-nøgle
    if (md5.equals("5fb3a9c4a1ebb853254fa1aebc37a89b")) return "0osb1BfVdrk1u8XJFAcAD0tA5hvcMFVbzInEgNQ";
    // Jacobs officielle nøgle
    if (md5.equals("d9a7385fd19107698149b7576fcb8b29")) return "0osb1BfVdrk3etct3WjSX-gUUayztcGvB51EMwg";

    // indsæt din egen nøgle her:
  }
Jacob Nordfalk
  • 3,533
  • 1
  • 21
  • 21
  • One question, does releaseSigHash in this example change for each version of the app that is signed? Or can you use the same signature over and over (my first app :) ) – Sandstone Nov 27 '11 at 22:27
  • This works well for using a 3rd party module but I can't see how an app can use this on itself. Its a chicken/egg problem on how to generate and verify the md5. – Sandstone Nov 30 '11 at 01:27
  • 1
    @Sandstone: No releaseSigHash will NEVER change, unless you chreate another developer key. That is the whole point! Run the code, print the sigHash that gets generated and put the value in a variable called releaseSigHash. I'm not sure how I can clarify so you get the point. Perhaps you just try it and see? – Jacob Nordfalk Dec 05 '11 at 13:17
  • Yes that works fine when your using another finished module like Google MapView, but try do to this on your own app from your own app. The very act of trying to generate/print a md5 will alter the md5 from what the final value should be. – Sandstone Dec 06 '11 at 04:11
-3

After some research/work we can up with a solution that checks against the singed cert.

        static public boolean DEVELOPMENT_VERBOSE = false;
        static private final  X500Principal RELEASE_DN = new X500Principal(
            "CN=aaa,OU=bbb,O=ccc,L=ddd,ST=eee,C=fff"
            );

        // auto disable the development logs if the apk is signed with a cert
        try {
            PackageManager manager = context.getPackageManager();
            PackageInfo appInfo = manager.getPackageInfo("com.xxx.app",
                    PackageManager.GET_SIGNATURES);
            Signature raw = appInfo.signatures[0];

            try {
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                X509Certificate cert = (X509Certificate) cf.generateCertificate(new ByteArrayInputStream(raw.toByteArray()));

                //DEVELOPMENT_VERBOSE = cert.getSubjectX500Principal().equals(DEBUG_DN);
                if (!cert.getSubjectX500Principal().equals(RELEASE_DN))
                    DEVELOPMENT_VERBOSE = true;

            } catch (CertificateException e) {  

            }           
        } catch (NameNotFoundException e) {

        }

As long as you use the same cert from version to version of the app this will always work.

Sandstone
  • 148
  • 2
  • 8
  • 2
    You are in essence doing exactly as I answered you below, but in a very complicated way. No need to generate X509 certificates or add data from your developer certificate into the code. Just use a hash. – Jacob Nordfalk Dec 05 '11 at 13:27