Merge "services/device: make device manager work on Brillo"
diff --git a/services/binary/tidy/impl_test.go b/services/binary/tidy/impl_test.go
index 9fbd5c6..9a80966 100644
--- a/services/binary/tidy/impl_test.go
+++ b/services/binary/tidy/impl_test.go
@@ -163,6 +163,11 @@
 			application.Envelope{},
 			fmt.Errorf("no applications.Match(darwin-amd64)"),
 		},
+		// applications.Match(android-arm)
+		appd.MatchResult{
+			application.Envelope{},
+			fmt.Errorf("no applications.Match(android-arm)"),
+		},
 		// applications/applicationd.Match(linux-amd64)
 		appd.MatchResult{
 			application.Envelope{
@@ -191,6 +196,11 @@
 			},
 			nil,
 		},
+		// applications/applicationd.Match(android-arm)
+		appd.MatchResult{
+			application.Envelope{},
+			fmt.Errorf("no applications/applicationd.Match(android-arm)"),
+		},
 		// applications/applicationd/0.Match(linux-amd64)
 		appd.MatchResult{
 			application.Envelope{
@@ -219,6 +229,11 @@
 			},
 			nil,
 		},
+		// applications/applicationd/0.Match(android-arm)
+		appd.MatchResult{
+			application.Envelope{},
+			fmt.Errorf("no applications/applicationd/0.Match(android-arm)"),
+		},
 		// applications/binaryd.Match(linux-amd64)
 		appd.MatchResult{
 			application.Envelope{
@@ -253,6 +268,11 @@
 			},
 			nil,
 		},
+		// applications/binaryd.Match(android-arm)
+		appd.MatchResult{
+			application.Envelope{},
+			fmt.Errorf("no applications/binaryd.Match(android-arm)"),
+		},
 		// applications/binaryd/1.Match(linux-amd64)
 		appd.MatchResult{
 			application.Envelope{
@@ -287,6 +307,11 @@
 			},
 			nil,
 		},
+		// applications/binaryd/1.Match(android-arm)
+		appd.MatchResult{
+			application.Envelope{},
+			fmt.Errorf("no applications/binaryd/1.Match(android-arm)"),
+		},
 	)
 
 	if err := v23cmd.ParseAndRunForTest(cmdBinaryTidy, ctx, env, []string{applicationName, binaryName}); err != nil {
@@ -333,22 +358,27 @@
 		appd.MatchStimulus{Name: "Match", Suffix: "applications", Profiles: []string{"linux-386"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications", Profiles: []string{"linux-arm"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications", Profiles: []string{"darwin-amd64"}},
+		appd.MatchStimulus{Name: "Match", Suffix: "applications", Profiles: []string{"android-arm"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/applicationd", Profiles: []string{"linux-amd64"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/applicationd", Profiles: []string{"linux-386"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/applicationd", Profiles: []string{"linux-arm"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/applicationd", Profiles: []string{"darwin-amd64"}},
+		appd.MatchStimulus{Name: "Match", Suffix: "applications/applicationd", Profiles: []string{"android-arm"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/applicationd/0", Profiles: []string{"linux-amd64"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/applicationd/0", Profiles: []string{"linux-386"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/applicationd/0", Profiles: []string{"linux-arm"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/applicationd/0", Profiles: []string{"darwin-amd64"}},
+		appd.MatchStimulus{Name: "Match", Suffix: "applications/applicationd/0", Profiles: []string{"android-arm"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/binaryd", Profiles: []string{"linux-amd64"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/binaryd", Profiles: []string{"linux-386"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/binaryd", Profiles: []string{"linux-arm"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/binaryd", Profiles: []string{"darwin-amd64"}},
+		appd.MatchStimulus{Name: "Match", Suffix: "applications/binaryd", Profiles: []string{"android-arm"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/binaryd/1", Profiles: []string{"linux-amd64"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/binaryd/1", Profiles: []string{"linux-386"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/binaryd/1", Profiles: []string{"linux-arm"}},
 		appd.MatchStimulus{Name: "Match", Suffix: "applications/binaryd/1", Profiles: []string{"darwin-amd64"}},
+		appd.MatchStimulus{Name: "Match", Suffix: "applications/binaryd/1", Profiles: []string{"android-arm"}},
 	}; !reflect.DeepEqual(expected, got) {
 		t.Errorf("apptape invalid call sequence. Got %#v, want %#v", got, expected)
 	}
diff --git a/services/device/deviced/internal/impl/device_service.go b/services/device/deviced/internal/impl/device_service.go
index 76a1a7f..6ee780b 100644
--- a/services/device/deviced/internal/impl/device_service.go
+++ b/services/device/deviced/internal/impl/device_service.go
@@ -416,7 +416,7 @@
 	}
 	stderrLog, stdoutLog := filepath.Join(logs, "STDERR"), filepath.Join(logs, "STDOUT")
 
-	output := "#!/bin/bash\n"
+	output := "#!" + ShellPath + "\n"
 	output += "if [ -z \"$DEVICE_MANAGER_DONT_REDIRECT_STDOUT_STDERR\" ]; then\n"
 	output += fmt.Sprintf("  TIMESTAMP=$(%s)\n", DateCommand)
 	output += fmt.Sprintf("  exec > %s-$TIMESTAMP 2> %s-$TIMESTAMP\n", stdoutLog, stderrLog)
diff --git a/services/device/deviced/internal/impl/helper_manager.go b/services/device/deviced/internal/impl/helper_manager.go
index 2d65694..4d635d2 100644
--- a/services/device/deviced/internal/impl/helper_manager.go
+++ b/services/device/deviced/internal/impl/helper_manager.go
@@ -26,16 +26,21 @@
 var suidHelper *suidHelperState
 
 func InitSuidHelper(ctx *context.T, helperPath string) {
-	if suidHelper != nil || helperPath == "" {
+	if suidHelper != nil {
 		return
 	}
+	if helperPath == "" {
+		ctx.Panicf("suidhelper path needs to be specified")
+	}
 
-	u, err := user.Current()
-	if err != nil {
-		ctx.Panicf("devicemanager has no current user: %v", err)
+	var userName string
+	if user, _ := user.Current(); user != nil && len(user.Username) > 0 {
+		userName = user.Username
+	} else {
+		userName = "anonymous"
 	}
 	suidHelper = &suidHelperState{
-		dmUser:     u.Username,
+		dmUser:     userName,
 		helperPath: helperPath,
 	}
 }
diff --git a/services/device/deviced/internal/impl/impl_test.go b/services/device/deviced/internal/impl/impl_test.go
index 81fdeba..b210344 100644
--- a/services/device/deviced/internal/impl/impl_test.go
+++ b/services/device/deviced/internal/impl/impl_test.go
@@ -53,7 +53,7 @@
 // of how device manager implementation sets up its updated versions.
 func generateDeviceManagerScript(t *testing.T, root string, args, env []string) string {
 	env = impl.VanadiumEnvironment(env)
-	output := "#!/bin/bash\n"
+	output := "#!" + impl.ShellPath + "\n"
 	output += strings.Join(config.QuoteEnv(env), " ") + " exec "
 	output += strings.Join(args, " ")
 	if err := os.MkdirAll(filepath.Join(root, "factory"), 0755); err != nil {
diff --git a/services/device/deviced/internal/impl/profile.go b/services/device/deviced/internal/impl/profile.go
index 9908666..5c978b1 100644
--- a/services/device/deviced/internal/impl/profile.go
+++ b/services/device/deviced/internal/impl/profile.go
@@ -40,6 +40,8 @@
 		result.Format = build.FormatElf
 	case build.OperatingSystemWindows:
 		result.Format = build.FormatPe
+	case build.OperatingSystemAndroid:
+		result.Format = build.FormatElf
 	default:
 		return nil, errors.New("Unsupported operating system: " + os.String())
 	}
@@ -91,6 +93,8 @@
 		// TODO(jsimsa): Implement.
 	case "windows":
 		// TODO(jsimsa): Implement.
+	case "android":
+		// TODO(caprita): Implement.
 	default:
 		return nil, errors.New("Unsupported operating system: " + runtime.GOOS)
 	}
diff --git a/services/device/deviced/internal/impl/shell_android.go b/services/device/deviced/internal/impl/shell_android.go
new file mode 100644
index 0000000..f45cf69
--- /dev/null
+++ b/services/device/deviced/internal/impl/shell_android.go
@@ -0,0 +1,10 @@
+// Copyright 2015 The Vanadium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package impl
+
+const (
+	ShellPath   = "/system/bin/sh"
+	DateCommand = "/system/bin/date +%s.$RANDOM"
+)
diff --git a/services/device/deviced/internal/impl/shell_darwin.go b/services/device/deviced/internal/impl/shell_darwin.go
index fc0d92a..e1b54a9 100644
--- a/services/device/deviced/internal/impl/shell_darwin.go
+++ b/services/device/deviced/internal/impl/shell_darwin.go
@@ -5,5 +5,6 @@
 package impl
 
 const (
+	ShellPath   = "/bin/bash"
 	DateCommand = "/bin/date +%s.$RANDOM"
 )
diff --git a/services/device/deviced/internal/impl/shell_linux.go b/services/device/deviced/internal/impl/shell_linux.go
index 8e02672..3b81a07 100644
--- a/services/device/deviced/internal/impl/shell_linux.go
+++ b/services/device/deviced/internal/impl/shell_linux.go
@@ -2,8 +2,11 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// +build linux,!android
+
 package impl
 
 const (
+	ShellPath   = "/bin/bash"
 	DateCommand = "/bin/date +%s%N"
 )
diff --git a/services/device/deviced/internal/impl/utiltest/helpers.go b/services/device/deviced/internal/impl/utiltest/helpers.go
index b8e855e..d66576a 100644
--- a/services/device/deviced/internal/impl/utiltest/helpers.go
+++ b/services/device/deviced/internal/impl/utiltest/helpers.go
@@ -498,7 +498,7 @@
 // GenerateSuidHelperScript builds a script to execute the test target as
 // a suidhelper instance and returns the path to the script.
 func GenerateSuidHelperScript(t *testing.T, root string) string {
-	output := "#!/bin/bash\n"
+	output := "#!" + impl.ShellPath + "\n"
 	output += "V23_SUIDHELPER_TEST=1"
 	output += " "
 	output += "exec " + os.Args[0] + " -minuid=1 -test.run=TestSuidHelper \"$@\""
@@ -519,8 +519,8 @@
 // GenerateAgentScript creates a simple script that acts as the security agent
 // for tests.  It blackholes arguments meant for the agent.
 func GenerateAgentScript(t *testing.T, root string) string {
-	output := `
-#!/bin/bash
+	output := "#!" + impl.ShellPath + "\n" +
+		`
 ARGS=$*
 for ARG in ${ARGS[@]}; do
   if [[ ${ARG} = -- ]]; then
diff --git a/services/device/deviced/internal/installer/device_installer.go b/services/device/deviced/internal/installer/device_installer.go
index fbdc36b..282dc51 100644
--- a/services/device/deviced/internal/installer/device_installer.go
+++ b/services/device/deviced/internal/installer/device_installer.go
@@ -218,7 +218,7 @@
 	}
 	stdoutLog, stderrLog := filepath.Join(logs, "STDOUT"), filepath.Join(logs, "STDERR")
 	// TODO(caprita): Switch all our generated bash scripts to use templates.
-	output := "#!/bin/bash\n"
+	output := "#!" + impl.ShellPath + "\n"
 	output += "if [ -z \"$DEVICE_MANAGER_DONT_REDIRECT_STDOUT_STDERR\" ]; then\n"
 	output += fmt.Sprintf("  TIMESTAMP=$(%s)\n", impl.DateCommand)
 	output += fmt.Sprintf("  exec > %s-$TIMESTAMP 2> %s-$TIMESTAMP\n", stdoutLog, stderrLog)
diff --git a/services/device/internal/suid/args.go b/services/device/internal/suid/args.go
index d9ab2fb..8ba2eeb 100644
--- a/services/device/internal/suid/args.go
+++ b/services/device/internal/suid/args.go
@@ -166,34 +166,37 @@
 		return nil
 	}
 
-	username := *flagUsername
-	if username == "" {
-		return verror.New(errUserNameMissing, nil)
-	}
+	if *flagDryrun {
+		wp.uid, wp.gid = -1, -1
+	} else {
+		username := *flagUsername
+		if username == "" {
+			return verror.New(errUserNameMissing, nil)
+		}
 
-	usr, err := user.Lookup(username)
-	if err != nil {
-		return verror.New(errUnknownUser, nil, username)
-	}
+		usr, err := user.Lookup(username)
+		if err != nil {
+			return verror.New(errUnknownUser, nil, username)
+		}
 
-	uid, err := strconv.ParseInt(usr.Uid, 0, 32)
-	if err != nil {
-		return verror.New(errInvalidUID, nil, usr.Uid)
-	}
-	gid, err := strconv.ParseInt(usr.Gid, 0, 32)
-	if err != nil {
-		return verror.New(errInvalidGID, nil, usr.Gid)
-	}
-	warnMissingSuidPrivs(int(uid))
+		uid, err := strconv.ParseInt(usr.Uid, 0, 32)
+		if err != nil {
+			return verror.New(errInvalidUID, nil, usr.Uid)
+		}
+		gid, err := strconv.ParseInt(usr.Gid, 0, 32)
+		if err != nil {
+			return verror.New(errInvalidGID, nil, usr.Gid)
+		}
+		warnMissingSuidPrivs(int(uid))
 
-	// Uids less than 501 can be special so we forbid running as them.
-	if uid < *flagMinimumUid {
-		return verror.New(errUIDTooLow, nil,
-			uid, *flagMinimumUid)
+		// Uids less than 501 can be special so we forbid running as them.
+		if uid < *flagMinimumUid {
+			return verror.New(errUIDTooLow, nil,
+				uid, *flagMinimumUid)
+		}
+		wp.uid = int(uid)
+		wp.gid = int(gid)
 	}
-	wp.uid = int(uid)
-	wp.gid = int(gid)
-
 	wp.dryrun = *flagDryrun
 
 	// At this point, all flags allowed by --chown have been processed
diff --git a/services/device/internal/suid/args_test.go b/services/device/internal/suid/args_test.go
index d910b51..b7ae0a8 100644
--- a/services/device/internal/suid/args_test.go
+++ b/services/device/internal/suid/args_test.go
@@ -103,8 +103,8 @@
 			[]string{"A=B"},
 			"",
 			WorkParameters{
-				uid:       testUid,
-				gid:       testGid,
+				uid:       -1,
+				gid:       -1,
 				workspace: "",
 				agentsock: "",
 				logDir:    "",
@@ -167,8 +167,8 @@
 			[]string{"A=B"},
 			"",
 			WorkParameters{
-				uid:       testUid,
-				gid:       testGid,
+				uid:       -1,
+				gid:       -1,
 				workspace: "/hello",
 				agentsock: "/tmp/2981298123/s",
 				logDir:    "/logging",
diff --git a/services/internal/profiles/listprofiles.go b/services/internal/profiles/listprofiles.go
index 6542bbe..66b6eb5 100644
--- a/services/internal/profiles/listprofiles.go
+++ b/services/internal/profiles/listprofiles.go
@@ -56,6 +56,13 @@
 			Os:          build.OperatingSystemDarwin,
 			Format:      build.FormatMach,
 		},
+		{
+			Label:       "android-arm",
+			Description: "",
+			Arch:        build.ArchitectureArm,
+			Os:          build.OperatingSystemAndroid,
+			Format:      build.FormatElf,
+		},
 	}, nil
 
 	// TODO(jsimsa): This function assumes the existence of a profile